mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
382 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d7eb054b6 | |||
| 11b90319f7 | |||
| 94b2a8cee0 | |||
| b55761829e | |||
| beba3c7c93 | |||
| 7d2980982c | |||
| b9c15af651 | |||
| 3824519a27 | |||
| adac95c7f9 | |||
| ea4a211216 | |||
| 9d08525567 | |||
| 5f0e0abbb8 | |||
| 455809858f | |||
| 2f9c7041fd | |||
| 5555e9f7dc | |||
| 763e3ca8ab | |||
| 2c1f348478 | |||
| c90eea2cd1 | |||
| d1731687e1 | |||
| 37ab6814f5 | |||
| 2a65a12fce | |||
| 5cd80525e7 | |||
| aaf07d8e4c | |||
| 070c09f2b3 | |||
| 9171401b88 | |||
| 9ebe59fa1c | |||
| c80f24d658 | |||
| c0e4ff140d | |||
| 79ff75fcc3 | |||
| f6e52f26c7 | |||
| b210d3ccd5 | |||
| 0e8e867b85 | |||
| 6545eff815 | |||
| 4fec563f7f | |||
| 1c6a209c7a | |||
| b144126612 | |||
| 77c60414a2 | |||
| d39e09da41 | |||
| 54f9269fad | |||
| fc946c5f72 | |||
| d99935a7ac | |||
| 880ebf4a71 | |||
| ae8a4c4905 | |||
| 9241615ad0 | |||
| a382066165 | |||
| f2bfa3151e | |||
| 6a55970373 | |||
| dc193a7e3a | |||
| 56db79a973 | |||
| 4c09fae588 | |||
| cf12993383 | |||
| 8b9df449c7 | |||
| 35de6f68f3 | |||
| 3441f7b914 | |||
| ae2fc18855 | |||
| 00bbd9b382 | |||
| e9a719ef2b | |||
| de51b9a48d | |||
| 1b36f6e1e0 | |||
| 8880ed67f1 | |||
| d7089d29ef | |||
| 12320347cc | |||
| fcdef1019f | |||
| 0e82d42774 | |||
| 8d183aebbb | |||
| 5b76da59c6 | |||
| 8ce68289e6 | |||
| 006ac7f22f | |||
| 635fb00969 | |||
| 8f68d1bec3 | |||
| 7cd89ad2c6 | |||
| 0c5b697afe | |||
| 792954ba2c | |||
| 27a678a093 | |||
| f90dc69548 | |||
| 670dc2af89 | |||
| 9604ec6e16 | |||
| 88a2c92f87 | |||
| 2d8943122b | |||
| 440b1ada9b | |||
| da9f509adf | |||
| d0c979e0f0 | |||
| f8ca1d57af | |||
| d725c6ed0f | |||
| 7edca03989 | |||
| 1fa95e5c2e | |||
| 1f02cb74e2 | |||
| bd1a755ab4 | |||
| 95037222fb | |||
| b3c7282121 | |||
| e816d2d694 | |||
| fa48f98d9f | |||
| 9eaeffd398 | |||
| f94f3a6acf | |||
| 0b7fc584d4 | |||
| 0e9e0348dd | |||
| 74cd7be6b3 | |||
| 3bc113d514 | |||
| 4387b37eb9 | |||
| c1a1b1c3f8 | |||
| 38557304e3 | |||
| 8d41c5bff3 | |||
| f15f41111b | |||
| b972249145 | |||
| b2222889c8 | |||
| f3361fbf3e | |||
| a689415d00 | |||
| d119cd32b1 | |||
| 51d7696b50 | |||
| db08558c3b | |||
| ad72be1059 | |||
| 139fead7d3 | |||
| e61d06afb5 | |||
| 086b4a986e | |||
| 84f3ba0d90 | |||
| fdde642985 | |||
| 11ed7e01e2 | |||
| dbbddd1617 | |||
| 0252da985a | |||
| 43d03f840e | |||
| 882f96fead | |||
| b18215d1c8 | |||
| 15f53f035b | |||
| 4b161e6234 | |||
| 8931fca34c | |||
| 958e98e4f7 | |||
| 90cef52107 | |||
| f4c4b10ea8 | |||
| 64da69f7b6 | |||
| ca5aa4d55b | |||
| da6e93f434 | |||
| b53220a9bf | |||
| 96b4f8abde | |||
| 8e7a6a32fa | |||
| f4b59770e1 | |||
| c2ef28062a | |||
| c64956c411 | |||
| bc79985c65 | |||
| 3bc8fe40e4 | |||
| d9420fedcb | |||
| 1ada286279 | |||
| 60492810fd | |||
| 2f96ba96a0 | |||
| 08fc82e13f | |||
| 0e156f7461 | |||
| 1b77d52dd7 | |||
| e2dca56aaf | |||
| 0ecdbdaafd | |||
| b982331dd3 | |||
| d9fc010b18 | |||
| 135ffc25b0 | |||
| 22a0e54ac4 | |||
| 7ff4235b42 | |||
| 5a59af0ef8 | |||
| 88d7b7c965 | |||
| ee46567475 | |||
| c9562ac2cc | |||
| 07565928ad | |||
| 650c179371 | |||
| 1b576ee5f5 | |||
| 4c16eae925 | |||
| 7a65b06665 | |||
| d6d3a87c72 | |||
| 854a66d8ee | |||
| 00a9331fe8 | |||
| 081f5f5a83 | |||
| 5e109c8ded | |||
| 9ac4057db8 | |||
| d6a1a3a5de | |||
| a2f622d82d | |||
| a4f144a366 | |||
| c5f28cd02e | |||
| 877456e729 | |||
| c667d54bc9 | |||
| 8e8ccc7927 | |||
| 867ea1c88a | |||
| bf899ef0f4 | |||
| c13c53fc65 | |||
| 9426e83988 | |||
| 6df6aa1ce2 | |||
| c422fb600a | |||
| 2be008986a | |||
| 2466fc9b42 | |||
| aa8ea7a5b2 | |||
| d306e31ee4 | |||
| c007dd5c77 | |||
| 1e06c8e8b6 | |||
| 13a8709b2b | |||
| 4a1ae82a6c | |||
| 9fea601a93 | |||
| dc2af3d54d | |||
| 7d476782dd | |||
| a0327bb596 | |||
| 5c19ab5511 | |||
| a38319ca39 | |||
| 9291d52856 | |||
| 9dffdd744d | |||
| 82fb14e600 | |||
| b4f3352232 | |||
| 2364be9ee2 | |||
| 0615ffb4f7 | |||
| acaadca289 | |||
| 9b8781a523 | |||
| 296aded9da | |||
| 24c5398f7d | |||
| f030409c88 | |||
| ca32602393 | |||
| 8551cabe23 | |||
| 9e1a1030c8 | |||
| 38566293a4 | |||
| 8caf9d7f77 | |||
| 8b888b2e52 | |||
| fc192fdba1 | |||
| 4a30a4e26b | |||
| 0d2b964d09 | |||
| 0f19351e2a | |||
| 7ee7eaa754 | |||
| 61139f617d | |||
| 9f9b1c7f8b | |||
| 7529512e7b | |||
| 05729f4116 | |||
| 41fd77664e | |||
| 044dac98c9 | |||
| 56b551d273 | |||
| 705fb0d236 | |||
| b6f22a7526 | |||
| 8ed63bc1bb | |||
| e98facebac | |||
| f9b6bf91be | |||
| c41b3c83ca | |||
| e59432cf07 | |||
| b144dd946e | |||
| 64bdffb97a | |||
| 7832be5905 | |||
| 4e1f28eddf | |||
| 36d740d6ef | |||
| 30f6db37a0 | |||
| 442ef134b8 | |||
| b1d0d20240 | |||
| 4e87b58fd1 | |||
| ec65169dfb | |||
| 15c42969fc | |||
| cfdadb562f | |||
| 23ca88522b | |||
| ea8008d95e | |||
| cef606aa8e | |||
| 44155c9209 | |||
| 43b89e7bb1 | |||
| 5e2f312321 | |||
| bd15db3960 | |||
| bde1b37e0b | |||
| 5131794cde | |||
| 70fb3bba4b | |||
| 5db2df8aad | |||
| bb12b30d52 | |||
| 0c105476a1 | |||
| 48dc0d6dc7 | |||
| 9d8e943e62 | |||
| 1a71167b5c | |||
| 83218bd0d1 | |||
| 5cc80dc72f | |||
| 9cdf950108 | |||
| 279e6b846f | |||
| 07cc97ff9b | |||
| 684e837758 | |||
| dad02c6535 | |||
| dfc8c432db | |||
| b967ca6b3b | |||
| 7bcdf7c143 | |||
| 484e0cf43e | |||
| 6b019ae958 | |||
| 6dc41265fd | |||
| 3076254567 | |||
| 90a16a1088 | |||
| 8d770eccee | |||
| 699733fcc3 | |||
| b76af47966 | |||
| 6accd1b0f5 | |||
| f08dbe0970 | |||
| 6c7493e39d | |||
| cc97128760 | |||
| 39e8bd0f4b | |||
| ba4b3dbed7 | |||
| d9e022dae2 | |||
| 48d899f423 | |||
| 19b6564cb2 | |||
| f24246ef25 | |||
| 65d2ca9ed3 | |||
| a2db87ef81 | |||
| 94ed3c3630 | |||
| a64a5755e8 | |||
| 20add2738c | |||
| e27a319261 | |||
| fbbe5d55b1 | |||
| 507ed58152 | |||
| 31bbbe9665 | |||
| 83abe5f606 | |||
| 26b3a8f206 | |||
| edae26e117 | |||
| 8579ff8352 | |||
| 2bffacbe5e | |||
| f24a8c60ad | |||
| 036ee75684 | |||
| 33a826b196 | |||
| 114658cf8c | |||
| d30ea1b89c | |||
| aff6d3d135 | |||
| 1c31c0d141 | |||
| 3f0116607d | |||
| 080fb08b76 | |||
| d3dde17693 | |||
| 9ae9d9adde | |||
| c615bdcde9 | |||
| 79c337448e | |||
| c67c551fb8 | |||
| 1149686da5 | |||
| 5c3be24712 | |||
| 444671ac5b | |||
| d1233ebfa8 | |||
| 1c328ac261 | |||
| cd31bfcc3e | |||
| 27174165a6 | |||
| bf89792f8b | |||
| 49e3ec0dcf | |||
| c2de5f68b3 | |||
| dd70675893 | |||
| 1dd94476b3 | |||
| c288a037b5 | |||
| b81ea4f672 | |||
| 1b3b05bf72 | |||
| cfad089cc3 | |||
| 95d227e5ef | |||
| ddfaf0cd46 | |||
| b09f4a5c79 | |||
| 6c0c012e08 | |||
| a16dfb1b36 | |||
| e549bdec8c | |||
| 16ca676db0 | |||
| 187758ccca | |||
| 330c24aed9 | |||
| ada75b605e | |||
| b06ed3cbf8 | |||
| e012cf0acf | |||
| 8e9463bffb | |||
| 9b4727587a | |||
| ee2f07fcff | |||
| f76fd48b4a | |||
| 5de4b0cb70 | |||
| fca21320e6 | |||
| c4126c7b9a | |||
| 176475f0fc | |||
| 7d6636087d | |||
| ce3a9a24d7 | |||
| ef17371ab6 | |||
| 6473214384 | |||
| f23946abe9 | |||
| fa6d9c22ac | |||
| d5f467e601 | |||
| b5f28006b3 | |||
| aac3e8acf6 | |||
| 10fb8722fe | |||
| 93c1918ab1 | |||
| ebfbaeba54 | |||
| 9c5a6a6442 | |||
| 67366d2f93 | |||
| 225e602176 | |||
| 02a768e1d2 | |||
| 35d20cb4b4 | |||
| 352021e1d2 | |||
| 285abbb38f | |||
| 3fd6551cae | |||
| ea8662ab3e | |||
| 1d28a42815 | |||
| 21740846b5 | |||
| 4fde15438f | |||
| 7a1bf0610e | |||
| 195d0b1ff5 | |||
| 053593ef3e | |||
| 347c3d5761 | |||
| 8449c071da | |||
| f88ed8e470 | |||
| 11ea0a0f7d |
@@ -113,13 +113,22 @@ build:remote --remote_instance_name=projects/internal-200822/instances/primary_i
|
||||
build:remote --bes_instance_name=internal-200822
|
||||
build:remote --remote_cache=remotebuildexecution.googleapis.com
|
||||
build:remote --remote_executor=remotebuildexecution.googleapis.com
|
||||
build:remote --remote_upload_local_results=false
|
||||
|
||||
build:remote --experimental_remote_grpc_log=/tmp/rbe-grpc.log
|
||||
|
||||
# See: https://docs.google.com/document/d/1NgDPsCIwprDdqC1zj0qQrh5KGK2hQTSTux1DAvi4rSc/edit?tab=t.0.
|
||||
build:remote --experimental_remote_execution_keepalive
|
||||
|
||||
# Use HTTP remote cache
|
||||
build:remote-cache --remote_cache=https://storage.googleapis.com/angular-team-cache
|
||||
build:remote-cache --remote_accept_cached=true
|
||||
build:remote-cache --remote_upload_local_results=true
|
||||
build:remote-cache --remote_upload_local_results=false
|
||||
build:remote-cache --google_default_credentials
|
||||
|
||||
# Additional flags added when running a "trusted build" with additional access
|
||||
build:trusted-build --remote_upload_local_results=true
|
||||
|
||||
# Ensure that tags like "no-remote-exec" get propagated to actions created by rules,
|
||||
# even if the rule implementation does not explicitly pass them to the execution requirements.
|
||||
# https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# all commits that change formatting to prettier
|
||||
fd544159e300340a25a48e3f40c58795991a4c17
|
||||
31fdf0fbea6b89c8d3d141b2ef8e79c2737287cb
|
||||
0d78a92431178be73342aeddda4b62d9e49138ba
|
||||
f307e95459898b9027d5e13357a809d8952e9017
|
||||
b1dffa4abe8321a47d79b2ea29ee32a81acfe031
|
||||
ca517d7f2c38775969a65fde1d88f41ad5f27931
|
||||
8f69c83b848ad0dc3c7ffce2541fc726693dedcf
|
||||
a5b5b7d5ef84b9852d2115dd7a764f4ab3299379
|
||||
894c1c5f100a62866cc7ed3e23d025c8e505c60d
|
||||
ca39926de2ccaceb11de47a35e5bf9899147a877
|
||||
b857aafcb9bdb5f1400a4cfedd56657e71ebe8cd
|
||||
c4b880a025823efadb9d76195c6261f2992c7654
|
||||
4c2b0d94e22f022969b86b95af516b76322840e0
|
||||
0460a9dfafe3a7ebbff1b87e52048fb7416bba43
|
||||
ec03e462f3d4ec90d1267474cc344563c949c2cf
|
||||
bbbe477f479f20722f0fea7ccc46095aad5d4253
|
||||
cc34e5fb072e7903e01463953a19d18503a39347
|
||||
f9781f9804f1e7c84df356d9aeb267730a7fc499
|
||||
dc62446ef7f2898a20d3be1196ecaf2a92a1077d
|
||||
711cb416260ca11ff158fc39b372efa2cf022d36
|
||||
# commits that switch to relative imports
|
||||
854a66d8eee2f0d0c1f4bafe976d191281004e4b
|
||||
d6d3a87c724a2b309870b04f1e8157c042642cc1
|
||||
7a65b06665425411094d8e1d64203d22b999255c
|
||||
1b576ee5f5be665a3663abd5df741f1ae90107f8
|
||||
650c179371973f9cfbf433b67f5fbf2722c55cf4
|
||||
c9562ac2cc28bbc4f78cd705c9437cb0a8057066
|
||||
ee46567475c2a485ac9abd07633f484a879379cf
|
||||
c64956c411a993fcac7c8daa57ab027a56d75ff3
|
||||
c2ef28062a9ae51e9143e5e2cb1ee75473f92696
|
||||
f4b59770e188071eb8a30eb077ba62c6ff550916
|
||||
8e7a6a32facc5ea6f375af01787bc13e468654b3
|
||||
96b4f8abde9c25fad713144a67617036e11dd977
|
||||
0b7fc584d4456944097ae1c94e7875492c21605a
|
||||
f94f3a6acf7050651842806db2440ca549309ba2
|
||||
9eaeffd3982923d1feea8ba7fbe44821dfff18e2
|
||||
1fa95e5c2e85825e77630ed15fbefbbe29cef6a3
|
||||
d725c6ed0f4b98b70fac702312b51b5c9cc412b6
|
||||
f8ca1d57af81b19c21b441c842c50a3c63da18e5
|
||||
@@ -1,7 +1,7 @@
|
||||
## PR Checklist
|
||||
Please check if your PR fulfills the following requirements:
|
||||
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
|
||||
@@ -3754,7 +3754,7 @@ var require_dist_node4 = __commonJS({
|
||||
if (options.request.headers.authorization) {
|
||||
requestCopy.headers = Object.assign({}, options.request.headers, {
|
||||
authorization: options.request.headers.authorization.replace(
|
||||
/ .*$/,
|
||||
/(?<! ) .*$/,
|
||||
" [REDACTED]"
|
||||
)
|
||||
});
|
||||
@@ -3814,7 +3814,7 @@ var require_dist_node5 = __commonJS({
|
||||
module.exports = __toCommonJS(dist_src_exports);
|
||||
var import_endpoint3 = require_dist_node2();
|
||||
var import_universal_user_agent7 = require_dist_node();
|
||||
var VERSION11 = "8.4.0";
|
||||
var VERSION11 = "8.4.1";
|
||||
function isPlainObject5(value) {
|
||||
if (typeof value !== "object" || value === null)
|
||||
return false;
|
||||
@@ -3863,7 +3863,7 @@ var require_dist_node5 = __commonJS({
|
||||
headers[keyAndValue[0]] = keyAndValue[1];
|
||||
}
|
||||
if ("deprecation" in headers) {
|
||||
const matches = headers.link && headers.link.match(/<([^>]+)>; rel="deprecation"/);
|
||||
const matches = headers.link && headers.link.match(/<([^<>]+)>; rel="deprecation"/);
|
||||
const deprecationLink = matches && matches.pop();
|
||||
log.warn(
|
||||
`[@octokit/request] "${requestOptions.method} ${requestOptions.url}" is deprecated. It is scheduled to be removed on ${headers.sunset}${deprecationLink ? `. See ${deprecationLink}` : ""}`
|
||||
@@ -4015,16 +4015,16 @@ var require_dist_node6 = __commonJS({
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
||||
var dist_src_exports = {};
|
||||
__export(dist_src_exports, {
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
GraphqlResponseError: () => GraphqlResponseError2,
|
||||
graphql: () => graphql22,
|
||||
withCustomRequest: () => withCustomRequest2
|
||||
});
|
||||
module.exports = __toCommonJS(dist_src_exports);
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
var import_request3 = require_dist_node5();
|
||||
var import_universal_user_agent7 = require_dist_node();
|
||||
var VERSION11 = "7.1.0";
|
||||
var VERSION11 = "7.1.1";
|
||||
var import_request22 = require_dist_node5();
|
||||
var import_request4 = require_dist_node5();
|
||||
function _buildMessageForResponseErrors2(data) {
|
||||
@@ -4224,17 +4224,17 @@ var require_dist_node8 = __commonJS({
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
||||
var dist_src_exports = {};
|
||||
__export(dist_src_exports, {
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
Octokit: () => Octokit3
|
||||
});
|
||||
module.exports = __toCommonJS(dist_src_exports);
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
var import_universal_user_agent7 = require_dist_node();
|
||||
var import_before_after_hook2 = require_before_after_hook();
|
||||
var import_request3 = require_dist_node5();
|
||||
var import_graphql2 = require_dist_node6();
|
||||
var import_auth_token2 = require_dist_node7();
|
||||
var VERSION11 = "5.2.0";
|
||||
var VERSION11 = "5.2.1";
|
||||
var noop2 = () => {
|
||||
};
|
||||
var consoleWarn2 = console.warn.bind(console);
|
||||
@@ -6533,7 +6533,7 @@ var require_dist_node10 = __commonJS({
|
||||
paginatingEndpoints: () => paginatingEndpoints
|
||||
});
|
||||
module.exports = __toCommonJS(dist_src_exports);
|
||||
var VERSION11 = "9.2.1";
|
||||
var VERSION11 = "9.2.2";
|
||||
function normalizePaginatedListResponse2(response) {
|
||||
if (!response.data) {
|
||||
return {
|
||||
@@ -6577,7 +6577,7 @@ var require_dist_node10 = __commonJS({
|
||||
const response = await requestMethod({ method, url, headers });
|
||||
const normalizedResponse = normalizePaginatedListResponse2(response);
|
||||
url = ((normalizedResponse.headers.link || "").match(
|
||||
/<([^>]+)>;\s*rel="next"/
|
||||
/<([^<>]+)>;\s*rel="next"/
|
||||
) || [])[1];
|
||||
return { value: normalizedResponse };
|
||||
} catch (error) {
|
||||
@@ -27620,7 +27620,7 @@ function paginateRest(octokit) {
|
||||
paginateRest.VERSION = VERSION8;
|
||||
|
||||
//
|
||||
var VERSION9 = "13.3.1";
|
||||
var VERSION9 = "13.5.0";
|
||||
|
||||
//
|
||||
var Endpoints = {
|
||||
@@ -27649,6 +27649,7 @@ var Endpoints = {
|
||||
createEnvironmentVariable: [
|
||||
"POST /repos/{owner}/{repo}/environments/{environment_name}/variables"
|
||||
],
|
||||
createHostedRunnerForOrg: ["POST /orgs/{org}/actions/hosted-runners"],
|
||||
createOrUpdateEnvironmentSecret: [
|
||||
"PUT /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name}"
|
||||
],
|
||||
@@ -27686,6 +27687,9 @@ var Endpoints = {
|
||||
deleteEnvironmentVariable: [
|
||||
"DELETE /repos/{owner}/{repo}/environments/{environment_name}/variables/{name}"
|
||||
],
|
||||
deleteHostedRunnerForOrg: [
|
||||
"DELETE /orgs/{org}/actions/hosted-runners/{hosted_runner_id}"
|
||||
],
|
||||
deleteOrgSecret: ["DELETE /orgs/{org}/actions/secrets/{secret_name}"],
|
||||
deleteOrgVariable: ["DELETE /orgs/{org}/actions/variables/{name}"],
|
||||
deleteRepoSecret: [
|
||||
@@ -27774,6 +27778,24 @@ var Endpoints = {
|
||||
getGithubActionsPermissionsRepository: [
|
||||
"GET /repos/{owner}/{repo}/actions/permissions"
|
||||
],
|
||||
getHostedRunnerForOrg: [
|
||||
"GET /orgs/{org}/actions/hosted-runners/{hosted_runner_id}"
|
||||
],
|
||||
getHostedRunnersGithubOwnedImagesForOrg: [
|
||||
"GET /orgs/{org}/actions/hosted-runners/images/github-owned"
|
||||
],
|
||||
getHostedRunnersLimitsForOrg: [
|
||||
"GET /orgs/{org}/actions/hosted-runners/limits"
|
||||
],
|
||||
getHostedRunnersMachineSpecsForOrg: [
|
||||
"GET /orgs/{org}/actions/hosted-runners/machine-sizes"
|
||||
],
|
||||
getHostedRunnersPartnerImagesForOrg: [
|
||||
"GET /orgs/{org}/actions/hosted-runners/images/partner"
|
||||
],
|
||||
getHostedRunnersPlatformsForOrg: [
|
||||
"GET /orgs/{org}/actions/hosted-runners/platforms"
|
||||
],
|
||||
getJobForWorkflowRun: ["GET /repos/{owner}/{repo}/actions/jobs/{job_id}"],
|
||||
getOrgPublicKey: ["GET /orgs/{org}/actions/secrets/public-key"],
|
||||
getOrgSecret: ["GET /orgs/{org}/actions/secrets/{secret_name}"],
|
||||
@@ -27817,6 +27839,10 @@ var Endpoints = {
|
||||
listEnvironmentVariables: [
|
||||
"GET /repos/{owner}/{repo}/environments/{environment_name}/variables"
|
||||
],
|
||||
listGithubHostedRunnersInGroupForOrg: [
|
||||
"GET /orgs/{org}/actions/runner-groups/{runner_group_id}/hosted-runners"
|
||||
],
|
||||
listHostedRunnersForOrg: ["GET /orgs/{org}/actions/hosted-runners"],
|
||||
listJobsForWorkflowRun: [
|
||||
"GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs"
|
||||
],
|
||||
@@ -27935,6 +27961,9 @@ var Endpoints = {
|
||||
updateEnvironmentVariable: [
|
||||
"PATCH /repos/{owner}/{repo}/environments/{environment_name}/variables/{name}"
|
||||
],
|
||||
updateHostedRunnerForOrg: [
|
||||
"PATCH /orgs/{org}/actions/hosted-runners/{hosted_runner_id}"
|
||||
],
|
||||
updateOrgVariable: ["PATCH /orgs/{org}/actions/variables/{name}"],
|
||||
updateRepoVariable: [
|
||||
"PATCH /repos/{owner}/{repo}/actions/variables/{name}"
|
||||
@@ -28452,6 +28481,26 @@ var Endpoints = {
|
||||
getAllTemplates: ["GET /gitignore/templates"],
|
||||
getTemplate: ["GET /gitignore/templates/{name}"]
|
||||
},
|
||||
hostedCompute: {
|
||||
createNetworkConfigurationForOrg: [
|
||||
"POST /orgs/{org}/settings/network-configurations"
|
||||
],
|
||||
deleteNetworkConfigurationFromOrg: [
|
||||
"DELETE /orgs/{org}/settings/network-configurations/{network_configuration_id}"
|
||||
],
|
||||
getNetworkConfigurationForOrg: [
|
||||
"GET /orgs/{org}/settings/network-configurations/{network_configuration_id}"
|
||||
],
|
||||
getNetworkSettingsForOrg: [
|
||||
"GET /orgs/{org}/settings/network-settings/{network_settings_id}"
|
||||
],
|
||||
listNetworkConfigurationsForOrg: [
|
||||
"GET /orgs/{org}/settings/network-configurations"
|
||||
],
|
||||
updateNetworkConfigurationForOrg: [
|
||||
"PATCH /orgs/{org}/settings/network-configurations/{network_configuration_id}"
|
||||
]
|
||||
},
|
||||
interactions: {
|
||||
getRestrictionsForAuthenticatedUser: ["GET /user/interaction-limits"],
|
||||
getRestrictionsForOrg: ["GET /orgs/{org}/interaction-limits"],
|
||||
@@ -28643,6 +28692,7 @@ var Endpoints = {
|
||||
"PUT /orgs/{org}/outside_collaborators/{username}"
|
||||
],
|
||||
createInvitation: ["POST /orgs/{org}/invitations"],
|
||||
createIssueType: ["POST /orgs/{org}/issue-types"],
|
||||
createOrUpdateCustomProperties: ["PATCH /orgs/{org}/properties/schema"],
|
||||
createOrUpdateCustomPropertiesValuesForRepos: [
|
||||
"PATCH /orgs/{org}/properties/values"
|
||||
@@ -28652,6 +28702,7 @@ var Endpoints = {
|
||||
],
|
||||
createWebhook: ["POST /orgs/{org}/hooks"],
|
||||
delete: ["DELETE /orgs/{org}"],
|
||||
deleteIssueType: ["DELETE /orgs/{org}/issue-types/{issue_type_id}"],
|
||||
deleteWebhook: ["DELETE /orgs/{org}/hooks/{hook_id}"],
|
||||
enableOrDisableSecurityProductOnAllOrgRepos: [
|
||||
"POST /orgs/{org}/{security_product}/{enablement}",
|
||||
@@ -28668,6 +28719,10 @@ var Endpoints = {
|
||||
getMembershipForAuthenticatedUser: ["GET /user/memberships/orgs/{org}"],
|
||||
getMembershipForUser: ["GET /orgs/{org}/memberships/{username}"],
|
||||
getOrgRole: ["GET /orgs/{org}/organization-roles/{role_id}"],
|
||||
getOrgRulesetHistory: ["GET /orgs/{org}/rulesets/{ruleset_id}/history"],
|
||||
getOrgRulesetVersion: [
|
||||
"GET /orgs/{org}/rulesets/{ruleset_id}/history/{version_id}"
|
||||
],
|
||||
getWebhook: ["GET /orgs/{org}/hooks/{hook_id}"],
|
||||
getWebhookConfigForOrg: ["GET /orgs/{org}/hooks/{hook_id}/config"],
|
||||
getWebhookDelivery: [
|
||||
@@ -28682,6 +28737,7 @@ var Endpoints = {
|
||||
listForAuthenticatedUser: ["GET /user/orgs"],
|
||||
listForUser: ["GET /users/{username}/orgs"],
|
||||
listInvitationTeams: ["GET /orgs/{org}/invitations/{invitation_id}/teams"],
|
||||
listIssueTypes: ["GET /orgs/{org}/issue-types"],
|
||||
listMembers: ["GET /orgs/{org}/members"],
|
||||
listMembershipsForAuthenticatedUser: ["GET /user/memberships/orgs"],
|
||||
listOrgRoleTeams: ["GET /orgs/{org}/organization-roles/{role_id}/teams"],
|
||||
@@ -28756,6 +28812,7 @@ var Endpoints = {
|
||||
],
|
||||
unblockUser: ["DELETE /orgs/{org}/blocks/{username}"],
|
||||
update: ["PATCH /orgs/{org}"],
|
||||
updateIssueType: ["PUT /orgs/{org}/issue-types/{issue_type_id}"],
|
||||
updateMembershipForAuthenticatedUser: [
|
||||
"PATCH /user/memberships/orgs/{org}"
|
||||
],
|
||||
@@ -28869,35 +28926,181 @@ var Endpoints = {
|
||||
]
|
||||
},
|
||||
projects: {
|
||||
addCollaborator: ["PUT /projects/{project_id}/collaborators/{username}"],
|
||||
createCard: ["POST /projects/columns/{column_id}/cards"],
|
||||
createColumn: ["POST /projects/{project_id}/columns"],
|
||||
createForAuthenticatedUser: ["POST /user/projects"],
|
||||
createForOrg: ["POST /orgs/{org}/projects"],
|
||||
createForRepo: ["POST /repos/{owner}/{repo}/projects"],
|
||||
delete: ["DELETE /projects/{project_id}"],
|
||||
deleteCard: ["DELETE /projects/columns/cards/{card_id}"],
|
||||
deleteColumn: ["DELETE /projects/columns/{column_id}"],
|
||||
get: ["GET /projects/{project_id}"],
|
||||
getCard: ["GET /projects/columns/cards/{card_id}"],
|
||||
getColumn: ["GET /projects/columns/{column_id}"],
|
||||
addCollaborator: [
|
||||
"PUT /projects/{project_id}/collaborators/{username}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.addCollaborator() is deprecated, see https://docs.github.com/rest/projects/collaborators#add-project-collaborator"
|
||||
}
|
||||
],
|
||||
createCard: [
|
||||
"POST /projects/columns/{column_id}/cards",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.createCard() is deprecated, see https://docs.github.com/rest/projects/cards#create-a-project-card"
|
||||
}
|
||||
],
|
||||
createColumn: [
|
||||
"POST /projects/{project_id}/columns",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.createColumn() is deprecated, see https://docs.github.com/rest/projects/columns#create-a-project-column"
|
||||
}
|
||||
],
|
||||
createForAuthenticatedUser: [
|
||||
"POST /user/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.createForAuthenticatedUser() is deprecated, see https://docs.github.com/rest/projects/projects#create-a-user-project"
|
||||
}
|
||||
],
|
||||
createForOrg: [
|
||||
"POST /orgs/{org}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.createForOrg() is deprecated, see https://docs.github.com/rest/projects/projects#create-an-organization-project"
|
||||
}
|
||||
],
|
||||
createForRepo: [
|
||||
"POST /repos/{owner}/{repo}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.createForRepo() is deprecated, see https://docs.github.com/rest/projects/projects#create-a-repository-project"
|
||||
}
|
||||
],
|
||||
delete: [
|
||||
"DELETE /projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.delete() is deprecated, see https://docs.github.com/rest/projects/projects#delete-a-project"
|
||||
}
|
||||
],
|
||||
deleteCard: [
|
||||
"DELETE /projects/columns/cards/{card_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.deleteCard() is deprecated, see https://docs.github.com/rest/projects/cards#delete-a-project-card"
|
||||
}
|
||||
],
|
||||
deleteColumn: [
|
||||
"DELETE /projects/columns/{column_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.deleteColumn() is deprecated, see https://docs.github.com/rest/projects/columns#delete-a-project-column"
|
||||
}
|
||||
],
|
||||
get: [
|
||||
"GET /projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.get() is deprecated, see https://docs.github.com/rest/projects/projects#get-a-project"
|
||||
}
|
||||
],
|
||||
getCard: [
|
||||
"GET /projects/columns/cards/{card_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.getCard() is deprecated, see https://docs.github.com/rest/projects/cards#get-a-project-card"
|
||||
}
|
||||
],
|
||||
getColumn: [
|
||||
"GET /projects/columns/{column_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.getColumn() is deprecated, see https://docs.github.com/rest/projects/columns#get-a-project-column"
|
||||
}
|
||||
],
|
||||
getPermissionForUser: [
|
||||
"GET /projects/{project_id}/collaborators/{username}/permission"
|
||||
"GET /projects/{project_id}/collaborators/{username}/permission",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.getPermissionForUser() is deprecated, see https://docs.github.com/rest/projects/collaborators#get-project-permission-for-a-user"
|
||||
}
|
||||
],
|
||||
listCards: [
|
||||
"GET /projects/columns/{column_id}/cards",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.listCards() is deprecated, see https://docs.github.com/rest/projects/cards#list-project-cards"
|
||||
}
|
||||
],
|
||||
listCollaborators: [
|
||||
"GET /projects/{project_id}/collaborators",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.listCollaborators() is deprecated, see https://docs.github.com/rest/projects/collaborators#list-project-collaborators"
|
||||
}
|
||||
],
|
||||
listColumns: [
|
||||
"GET /projects/{project_id}/columns",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.listColumns() is deprecated, see https://docs.github.com/rest/projects/columns#list-project-columns"
|
||||
}
|
||||
],
|
||||
listForOrg: [
|
||||
"GET /orgs/{org}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.listForOrg() is deprecated, see https://docs.github.com/rest/projects/projects#list-organization-projects"
|
||||
}
|
||||
],
|
||||
listForRepo: [
|
||||
"GET /repos/{owner}/{repo}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.listForRepo() is deprecated, see https://docs.github.com/rest/projects/projects#list-repository-projects"
|
||||
}
|
||||
],
|
||||
listForUser: [
|
||||
"GET /users/{username}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.listForUser() is deprecated, see https://docs.github.com/rest/projects/projects#list-user-projects"
|
||||
}
|
||||
],
|
||||
moveCard: [
|
||||
"POST /projects/columns/cards/{card_id}/moves",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.moveCard() is deprecated, see https://docs.github.com/rest/projects/cards#move-a-project-card"
|
||||
}
|
||||
],
|
||||
moveColumn: [
|
||||
"POST /projects/columns/{column_id}/moves",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.moveColumn() is deprecated, see https://docs.github.com/rest/projects/columns#move-a-project-column"
|
||||
}
|
||||
],
|
||||
listCards: ["GET /projects/columns/{column_id}/cards"],
|
||||
listCollaborators: ["GET /projects/{project_id}/collaborators"],
|
||||
listColumns: ["GET /projects/{project_id}/columns"],
|
||||
listForOrg: ["GET /orgs/{org}/projects"],
|
||||
listForRepo: ["GET /repos/{owner}/{repo}/projects"],
|
||||
listForUser: ["GET /users/{username}/projects"],
|
||||
moveCard: ["POST /projects/columns/cards/{card_id}/moves"],
|
||||
moveColumn: ["POST /projects/columns/{column_id}/moves"],
|
||||
removeCollaborator: [
|
||||
"DELETE /projects/{project_id}/collaborators/{username}"
|
||||
"DELETE /projects/{project_id}/collaborators/{username}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.removeCollaborator() is deprecated, see https://docs.github.com/rest/projects/collaborators#remove-user-as-a-collaborator"
|
||||
}
|
||||
],
|
||||
update: ["PATCH /projects/{project_id}"],
|
||||
updateCard: ["PATCH /projects/columns/cards/{card_id}"],
|
||||
updateColumn: ["PATCH /projects/columns/{column_id}"]
|
||||
update: [
|
||||
"PATCH /projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.update() is deprecated, see https://docs.github.com/rest/projects/projects#update-a-project"
|
||||
}
|
||||
],
|
||||
updateCard: [
|
||||
"PATCH /projects/columns/cards/{card_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.updateCard() is deprecated, see https://docs.github.com/rest/projects/cards#update-an-existing-project-card"
|
||||
}
|
||||
],
|
||||
updateColumn: [
|
||||
"PATCH /projects/columns/{column_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.projects.updateColumn() is deprecated, see https://docs.github.com/rest/projects/columns#update-an-existing-project-column"
|
||||
}
|
||||
]
|
||||
},
|
||||
pulls: {
|
||||
checkIfMerged: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/merge"],
|
||||
@@ -29270,6 +29473,12 @@ var Endpoints = {
|
||||
],
|
||||
getRepoRuleSuites: ["GET /repos/{owner}/{repo}/rulesets/rule-suites"],
|
||||
getRepoRuleset: ["GET /repos/{owner}/{repo}/rulesets/{ruleset_id}"],
|
||||
getRepoRulesetHistory: [
|
||||
"GET /repos/{owner}/{repo}/rulesets/{ruleset_id}/history"
|
||||
],
|
||||
getRepoRulesetVersion: [
|
||||
"GET /repos/{owner}/{repo}/rulesets/{ruleset_id}/history/{version_id}"
|
||||
],
|
||||
getRepoRulesets: ["GET /repos/{owner}/{repo}/rulesets"],
|
||||
getStatusChecksProtection: [
|
||||
"GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"
|
||||
@@ -29443,7 +29652,13 @@ var Endpoints = {
|
||||
search: {
|
||||
code: ["GET /search/code"],
|
||||
commits: ["GET /search/commits"],
|
||||
issuesAndPullRequests: ["GET /search/issues"],
|
||||
issuesAndPullRequests: [
|
||||
"GET /search/issues",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.search.issuesAndPullRequests() is deprecated, see https://docs.github.com/rest/search/search#search-issues-and-pull-requests"
|
||||
}
|
||||
],
|
||||
labels: ["GET /search/labels"],
|
||||
repos: ["GET /search/repositories"],
|
||||
topics: ["GET /search/topics"],
|
||||
@@ -29498,13 +29713,35 @@ var Endpoints = {
|
||||
"PUT /orgs/{org}/teams/{team_slug}/memberships/{username}"
|
||||
],
|
||||
addOrUpdateProjectPermissionsInOrg: [
|
||||
"PUT /orgs/{org}/teams/{team_slug}/projects/{project_id}"
|
||||
"PUT /orgs/{org}/teams/{team_slug}/projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.addOrUpdateProjectPermissionsInOrg() is deprecated, see https://docs.github.com/rest/teams/teams#add-or-update-team-project-permissions"
|
||||
}
|
||||
],
|
||||
addOrUpdateProjectPermissionsLegacy: [
|
||||
"PUT /teams/{team_id}/projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.addOrUpdateProjectPermissionsLegacy() is deprecated, see https://docs.github.com/rest/teams/teams#add-or-update-team-project-permissions-legacy"
|
||||
}
|
||||
],
|
||||
addOrUpdateRepoPermissionsInOrg: [
|
||||
"PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"
|
||||
],
|
||||
checkPermissionsForProjectInOrg: [
|
||||
"GET /orgs/{org}/teams/{team_slug}/projects/{project_id}"
|
||||
"GET /orgs/{org}/teams/{team_slug}/projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.checkPermissionsForProjectInOrg() is deprecated, see https://docs.github.com/rest/teams/teams#check-team-permissions-for-a-project"
|
||||
}
|
||||
],
|
||||
checkPermissionsForProjectLegacy: [
|
||||
"GET /teams/{team_id}/projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.checkPermissionsForProjectLegacy() is deprecated, see https://docs.github.com/rest/teams/teams#check-team-permissions-for-a-project-legacy"
|
||||
}
|
||||
],
|
||||
checkPermissionsForRepoInOrg: [
|
||||
"GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"
|
||||
@@ -29542,13 +29779,37 @@ var Endpoints = {
|
||||
listPendingInvitationsInOrg: [
|
||||
"GET /orgs/{org}/teams/{team_slug}/invitations"
|
||||
],
|
||||
listProjectsInOrg: ["GET /orgs/{org}/teams/{team_slug}/projects"],
|
||||
listProjectsInOrg: [
|
||||
"GET /orgs/{org}/teams/{team_slug}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.listProjectsInOrg() is deprecated, see https://docs.github.com/rest/teams/teams#list-team-projects"
|
||||
}
|
||||
],
|
||||
listProjectsLegacy: [
|
||||
"GET /teams/{team_id}/projects",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.listProjectsLegacy() is deprecated, see https://docs.github.com/rest/teams/teams#list-team-projects-legacy"
|
||||
}
|
||||
],
|
||||
listReposInOrg: ["GET /orgs/{org}/teams/{team_slug}/repos"],
|
||||
removeMembershipForUserInOrg: [
|
||||
"DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}"
|
||||
],
|
||||
removeProjectInOrg: [
|
||||
"DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id}"
|
||||
"DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.removeProjectInOrg() is deprecated, see https://docs.github.com/rest/teams/teams#remove-a-project-from-a-team"
|
||||
}
|
||||
],
|
||||
removeProjectLegacy: [
|
||||
"DELETE /teams/{team_id}/projects/{project_id}",
|
||||
{},
|
||||
{
|
||||
deprecated: "octokit.rest.teams.removeProjectLegacy() is deprecated, see https://docs.github.com/rest/teams/teams#remove-a-project-from-a-team-legacy"
|
||||
}
|
||||
],
|
||||
removeRepoInOrg: [
|
||||
"DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"
|
||||
@@ -29830,7 +30091,7 @@ function legacyRestEndpointMethods(octokit) {
|
||||
legacyRestEndpointMethods.VERSION = VERSION9;
|
||||
|
||||
//
|
||||
var VERSION10 = "21.1.0";
|
||||
var VERSION10 = "21.1.1";
|
||||
|
||||
//
|
||||
var Octokit2 = Octokit.plugin(requestLog, legacyRestEndpointMethods, paginateRest).defaults(
|
||||
|
||||
@@ -5,9 +5,9 @@ runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Saucelabs Variables
|
||||
uses: angular/dev-infra/github-actions/saucelabs@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/saucelabs@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Starting Saucelabs tunnel service
|
||||
shell: bash
|
||||
run: ./tools/saucelabs/sauce-service.sh run &
|
||||
|
||||
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||
with:
|
||||
path: |
|
||||
./node_modules/
|
||||
|
||||
@@ -21,16 +21,16 @@ jobs:
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: yarn bazel build //adev:build --full_build_adev --config=release
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
pull-number: '${{github.event.pull_request.number}}'
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
|
||||
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
|
||||
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
github-token: '${{secrets.GITHUB_TOKEN}}'
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: angular/dev-infra/github-actions/branch-manager@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/branch-manager@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
- uses: ./.github/actions/yarn-install
|
||||
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
bazelrc: ./.bazelrc.user
|
||||
|
||||
|
||||
+48
-20
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
@@ -41,13 +41,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Run unit tests
|
||||
@@ -59,28 +61,50 @@ jobs:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run CI tests for framework
|
||||
run: yarn test:ci
|
||||
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run integration CI tests for framework
|
||||
run: yarn integration-tests:ci
|
||||
|
||||
adev:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev in fast mode to ensure it continues to work
|
||||
@@ -95,13 +119,15 @@ jobs:
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
|
||||
@@ -113,7 +139,7 @@ jobs:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
@@ -121,9 +147,11 @@ jobs:
|
||||
./packages/zone.js/node_modules
|
||||
./packages/zone.js/test/typings/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- run: |
|
||||
@@ -160,7 +188,7 @@ jobs:
|
||||
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
@@ -173,11 +201,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
|
||||
@@ -13,13 +13,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
post_approval_changes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: angular/dev-infra/github-actions/google-internal-tests@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/google-internal-tests@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
run-tests-guide-url: http://go/angular-g3sync-start
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -13,17 +13,17 @@ jobs:
|
||||
JOBS: 2
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Saucelabs Variables
|
||||
uses: angular/dev-infra/github-actions/saucelabs@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/saucelabs@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Set up Sauce Tunnel Daemon
|
||||
run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
|
||||
env:
|
||||
|
||||
@@ -9,6 +9,6 @@ jobs:
|
||||
status:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/unified-status-check@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
- uses: angular/dev-infra/github-actions/unified-status-check@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
workflows: ${{ steps.workflows.outputs.workflows }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn -s install --frozen-lockfile
|
||||
- id: workflows
|
||||
@@ -36,9 +36,9 @@ jobs:
|
||||
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn -s install --frozen-lockfile
|
||||
# We utilize the google-github-actions/auth action to allow us to get an active credential using workflow
|
||||
|
||||
+41
-19
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Check code format
|
||||
run: yarn ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
|
||||
- name: Check Package Licenses
|
||||
uses: angular/dev-infra/github-actions/linting/licenses@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/linting/licenses@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
allow-dependencies-licenses: 'pkg:npm/google-protobuf@'
|
||||
|
||||
@@ -47,13 +47,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Run unit tests
|
||||
@@ -65,17 +65,39 @@ jobs:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run CI tests for framework
|
||||
run: yarn test:ci
|
||||
- name: Upload GRPC logs (for debugging of RBE issues)
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
path: /tmp/rbe-grpc.log
|
||||
retention-days: 1
|
||||
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run integration CI tests for framework
|
||||
run: yarn integration-tests:ci
|
||||
|
||||
artifacts:
|
||||
needs: [test]
|
||||
@@ -83,19 +105,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run CI tests for framework
|
||||
run: yarn tsx ./scripts/build/build-packages-dist.mts
|
||||
- name: Archive build artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
with:
|
||||
name: pr-artifacts-${{ github.event.number }}
|
||||
path: dist/packages-dist/
|
||||
@@ -105,11 +127,11 @@ jobs:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev in fast mode to ensure it continues to work
|
||||
@@ -124,7 +146,7 @@ jobs:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
@@ -132,9 +154,9 @@ jobs:
|
||||
./packages/zone.js/node_modules
|
||||
./packages/zone.js/test/typings/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- run: |
|
||||
@@ -171,7 +193,7 @@ jobs:
|
||||
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Run analysis'
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
# Upload the results as artifacts.
|
||||
- name: 'Upload artifact'
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -47,6 +47,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
|
||||
uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
env:
|
||||
ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }}
|
||||
- name: Create a PR (if necessary)
|
||||
uses: angular/dev-infra/github-actions/create-pr-for-changes@002a34e9875f55bf1850fb57d34261b33b2fd492
|
||||
uses: angular/dev-infra/github-actions/create-pr-for-changes@b3b3466509babc50fd7f00b90e8bc607e7fe41aa
|
||||
with:
|
||||
branch-prefix: update-cli-help
|
||||
pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]'
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ Fixes #<issue number>
|
||||
# =============================
|
||||
#
|
||||
# The full specification of the Angular Commit Message Format can be found at
|
||||
# https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit
|
||||
# https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md
|
||||
#
|
||||
# The following is an excerpt of the specification with the most commonly needed info.
|
||||
#
|
||||
|
||||
@@ -7,6 +7,8 @@ export const commitMessage: CommitMessageConfig = {
|
||||
maxLineLength: Infinity,
|
||||
minBodyLength: 20,
|
||||
minBodyLengthTypeExcludes: ['docs'],
|
||||
// If you update this, also update the docs.
|
||||
// https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md#scope
|
||||
scopes: [
|
||||
'animations',
|
||||
'bazel',
|
||||
|
||||
+3
-29
@@ -144,7 +144,6 @@ groups:
|
||||
- >
|
||||
contains_any_globs(files.exclude('packages/core/primitives/*'), [
|
||||
'contributing-docs/public-api-surface.md',
|
||||
'goldens/circular-deps/packages.json',
|
||||
'integration/**/{*,.*}',
|
||||
'modules/**/{*,.*}',
|
||||
'packages/animations/**/{*,.*}',
|
||||
@@ -288,6 +287,7 @@ groups:
|
||||
- MarkTechson
|
||||
- kirjs
|
||||
- mmalerba
|
||||
- ~hawkgs
|
||||
|
||||
# =========================================================
|
||||
# Angular DevTools
|
||||
@@ -395,6 +395,8 @@ groups:
|
||||
- pkozlowski-opensource
|
||||
- kirjs
|
||||
- mmalerba
|
||||
- crisbeto
|
||||
- devversion
|
||||
- ~iteriani
|
||||
- ~tbondwilkinson
|
||||
- ~rahatarmanahmed
|
||||
@@ -430,28 +432,6 @@ groups:
|
||||
required: 1 # Require that 1 person approve
|
||||
reviewed_for: required
|
||||
|
||||
# ================================================
|
||||
# Circular dependencies
|
||||
# ================================================
|
||||
circular-dependencies:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *no-groups-above-this-pending
|
||||
- *no-groups-above-this-rejected
|
||||
- >
|
||||
contains_any_globs(files, [
|
||||
'goldens/circular-deps/packages.json'
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- alxhub
|
||||
- AndrewKushnir
|
||||
- atscott
|
||||
- ~jelbourn
|
||||
- thePunderWoman
|
||||
- pkozlowski-opensource
|
||||
- kirjs
|
||||
|
||||
####################################################################################
|
||||
# Special Cases
|
||||
####################################################################################
|
||||
@@ -511,13 +491,7 @@ groups:
|
||||
reviewers:
|
||||
users:
|
||||
- csmick # Cameron Smick
|
||||
- lannka # Hongfei Ding
|
||||
- eduhmc # Eduardo Huerta-Mercado
|
||||
- ehlemur # Edward Lesmes
|
||||
- ellenyuan # Ellen Yuan
|
||||
- jesse-good # Jesse Costello-Good
|
||||
- mturco # Matt Turco
|
||||
- neonstalwart # Ben Hockey
|
||||
- iteriani # Thomas Nguyen
|
||||
- tbondwilkinson # Tom Wilkinson
|
||||
- rahatarmanahmed # Rahat Ahmed
|
||||
|
||||
Vendored
+8
-5
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
// Exclude third party modules and build artifacts from the editor watchers/searches.
|
||||
"files.watcherExclude": {
|
||||
@@ -11,14 +11,17 @@
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/bazel-out/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/dist/**": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/bower_components": true,
|
||||
"**/bazel-out": true,
|
||||
"**/dist": true,
|
||||
".history": true,
|
||||
".history": true
|
||||
},
|
||||
"git.ignoreLimitWarning": true,
|
||||
}
|
||||
"gitlens.advanced.blame.customArguments": [
|
||||
"--ignore-revs-file .git-blame-ignore-revs"
|
||||
]
|
||||
}
|
||||
|
||||
+235
-101
@@ -1,3 +1,238 @@
|
||||
<a name="19.2.8"></a>
|
||||
# 19.2.8 (2025-04-23)
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ea4a211216](https://github.com/angular/angular/commit/ea4a21121681c78652f314c78c58390dca25f266) | fix | make NgForm emit FormSubmittedEvent and FormResetEvent ([#60887](https://github.com/angular/angular/pull/60887)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.7"></a>
|
||||
# 19.2.7 (2025-04-16)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [37ab6814f5](https://github.com/angular/angular/commit/37ab6814f5485434d9642b9f9c28dd430864247b) | fix | issue a warning instead of an error when `NgOptimizedImage` exceeds the preload limit ([#60883](https://github.com/angular/angular/pull/60883)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b144126612](https://github.com/angular/angular/commit/b144126612e2cd14cbccc8d3cf4e2136a2e540ff) | fix | inject migration: replace param with this. ([#60713](https://github.com/angular/angular/pull/60713)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d39e09da41](https://github.com/angular/angular/commit/d39e09da413732385a12ed21eb468649233e26d0) | fix | Include HTTP status code and headers when HTTP requests errored in `httpResource` ([#60802](https://github.com/angular/angular/pull/60802)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.6"></a>
|
||||
# 19.2.6 (2025-04-09)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [3441f7b914](https://github.com/angular/angular/commit/3441f7b914c73ccdaacbcd935e945dc304c5962a) | fix | error if rawText isn't estimated correctly ([#60529](https://github.com/angular/angular/pull/60529)) ([#60753](https://github.com/angular/angular/pull/60753)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fc946c5f72](https://github.com/angular/angular/commit/fc946c5f7261ee3e49fa037bc55703b9ffcfbff3) | fix | ensure HMR works with different output module type ([#60797](https://github.com/angular/angular/pull/60797)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [f2bfa3151e](https://github.com/angular/angular/commit/f2bfa3151ee7ecb335665d55741387bd67ebee9d) | fix | fix ng generate @angular/core:output-migration. Fixes angular[#58650](https://github.com/angular/angular/pull/58650) ([#60763](https://github.com/angular/angular/pull/60763)) |
|
||||
| [9241615ad0](https://github.com/angular/angular/commit/9241615ad0825156f4bf31bc4308372e4789e902) | fix | reduce total memory usage of various migration schematics ([#60776](https://github.com/angular/angular/pull/60776)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0e82d42774](https://github.com/angular/angular/commit/0e82d427743c1d22e1683da11f66e33846f38663) | fix | Do not provide element completions in end tag ([#60616](https://github.com/angular/angular/pull/60616)) |
|
||||
| [fcdef1019f](https://github.com/angular/angular/commit/fcdef1019fd28c7261590ba484a949c809b9ceaf) | fix | Ensure dollar signs are escaped in completions ([#60597](https://github.com/angular/angular/pull/60597)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.5"></a>
|
||||
# 19.2.5 (2025-04-02)
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e61d06afb5](https://github.com/angular/angular/commit/e61d06afb5f68268b204bb2630930bb213620811) | fix | step 6 tutorial docs ([#60630](https://github.com/angular/angular/pull/60630)) |
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fa48f98d9f](https://github.com/angular/angular/commit/fa48f98d9f7e7b74deba65bea9bc90843b1c283b) | fix | add missing peer dependency on `@angular/common` ([#60660](https://github.com/angular/angular/pull/60660)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ca5aa4d55b](https://github.com/angular/angular/commit/ca5aa4d55b352d1ead43d78b6a74d9e3b57f8777) | fix | throw for invalid "as" expression in if block ([#60580](https://github.com/angular/angular/pull/60580)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [f4c4b10ea8](https://github.com/angular/angular/commit/f4c4b10ea8dc263c30d1051a83a72486344d81e4) | fix | Produce fatal diagnostic on duplicate decorated properties ([#60376](https://github.com/angular/angular/pull/60376)) |
|
||||
| [22a0e54ac4](https://github.com/angular/angular/commit/22a0e54ac4ae7b943740dc314ff7f26ee7530ee5) | fix | support relative imports to symbols outside `rootDir` ([#60555](https://github.com/angular/angular/pull/60555)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [64da69f7b6](https://github.com/angular/angular/commit/64da69f7b68c906544b3cb76b2fc7ec42cc434a8) | fix | check ngDevMode for undefined ([#60565](https://github.com/angular/angular/pull/60565)) |
|
||||
| [8f68d1bec3](https://github.com/angular/angular/commit/8f68d1bec320c8af4c28ad6c8dbeda4581e8299b) | fix | fix ng generate @angular/core:output-migration ([#60626](https://github.com/angular/angular/pull/60626)) |
|
||||
| [bc79985c65](https://github.com/angular/angular/commit/bc79985c65c38dee17f5cb53f2a14768632f72dc) | fix | fix regexp for event types ([#60592](https://github.com/angular/angular/pull/60592)) |
|
||||
| [006ac7f22f](https://github.com/angular/angular/commit/006ac7f22f47b110129ca603cfa34f5514d7b4dd) | fix | fixes [#592882](https://github.com/angular/angular/pull/592882) ng generate @angular/core:signal-queries-migration ([#60688](https://github.com/angular/angular/pull/60688)) |
|
||||
| [da6e93f434](https://github.com/angular/angular/commit/da6e93f4341804cd16327596c30d8f9258b40d7e) | fix | preserve comments in internal inject migration ([#60588](https://github.com/angular/angular/pull/60588)) |
|
||||
| [dbbddd1617](https://github.com/angular/angular/commit/dbbddd161721990f29a037f88f930b333712550e) | fix | prevent omission of deferred pipes in full compilation ([#60571](https://github.com/angular/angular/pull/60571)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0e9e0348dd](https://github.com/angular/angular/commit/0e9e0348dd972c96ecee8bb990aba8c604dc704f) | fix | Update adapter to log instead of throw errors ([#60651](https://github.com/angular/angular/pull/60651)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [15f53f035b](https://github.com/angular/angular/commit/15f53f035ba64fa761db64d48dd9daa1499370e3) | fix | handle shorthand assignments in super call ([#60602](https://github.com/angular/angular/pull/60602)) |
|
||||
| [4b161e6234](https://github.com/angular/angular/commit/4b161e62344a51e87f1b5c5778fd72e56fc37922) | fix | inject migration not handling super parameter referenced via this ([#60602](https://github.com/angular/angular/pull/60602)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [958e98e4f7](https://github.com/angular/angular/commit/958e98e4f7ab8e708440f03eb68612d1802b9a71) | fix | Add missing types to transition ([#60307](https://github.com/angular/angular/pull/60307)) |
|
||||
### service-worker
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [7cd89ad2c6](https://github.com/angular/angular/commit/7cd89ad2c66adeb625f75d23fea32e762162d3d5) | fix | assign initializing client's app version, when a request is for worker script ([#58131](https://github.com/angular/angular/pull/58131)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.4"></a>
|
||||
# 19.2.4 (2025-03-26)
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [081f5f5a83f](https://github.com/angular/angular/commit/081f5f5a83fef99718952519bed9fe39005d6d37) | fix | fix used templates are not deleted ([#60459](https://github.com/angular/angular/pull/60459)) |
|
||||
### localize
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a2f622d82d6](https://github.com/angular/angular/commit/a2f622d82d6c0f93a5fdf34fc8e5829db04c7380) | fix | handle @angular/build:karma in ng add ([#60513](https://github.com/angular/angular/pull/60513)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8e8ccc79279](https://github.com/angular/angular/commit/8e8ccc792790c876cf5831f3d7a504290c665a1b) | fix | ensure `platformBrowserTesting` includes `platformBrowser` providers ([#60480](https://github.com/angular/angular/pull/60480)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.3"></a>
|
||||
# 19.2.3 (2025-03-19)
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [aa8ea7a5b2](https://github.com/angular/angular/commit/aa8ea7a5b227913e3f15270dac48479481c47f9a) | fix | report more accurate diagnostic for invalid import ([#60455](https://github.com/angular/angular/pull/60455)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [13a8709b2b](https://github.com/angular/angular/commit/13a8709b2ba29ef3ab7d38bfb41cdd3d44c24c51) | fix | catch hydration marker with implicit body tag ([#60429](https://github.com/angular/angular/pull/60429)) |
|
||||
| [296aded9da](https://github.com/angular/angular/commit/296aded9daaf04edd3cda623a220cbd2bf57e0f1) | fix | execute timer trigger outside zone ([#60392](https://github.com/angular/angular/pull/60392)) |
|
||||
| [0615ffb4f7](https://github.com/angular/angular/commit/0615ffb4f7a41cca2a408419830411897d1826e6) | fix | include input name in error message ([#60404](https://github.com/angular/angular/pull/60404)) |
|
||||
### platform-browser-dynamic
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1e06c8e8b6](https://github.com/angular/angular/commit/1e06c8e8b6473a19584ceb66945fd435ea4e6af9) | fix | ensure compiler is loaded before `@angular/common` ([#60458](https://github.com/angular/angular/pull/60458)) |
|
||||
### upgrade
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9e1a1030c8](https://github.com/angular/angular/commit/9e1a1030c818e9849c00ded5f79535ea56976ea8) | fix | handle output emitters when downgrading a component ([#60369](https://github.com/angular/angular/pull/60369)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.2"></a>
|
||||
# 19.2.2 (2025-03-12)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [90a16a1088](https://github.com/angular/angular/commit/90a16a10888eee37d8a61cdbfad070e002a3cfdf) | fix | support equality function in httpResource ([#60026](https://github.com/angular/angular/pull/60026)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [56b551d273](https://github.com/angular/angular/commit/56b551d273a0978e1f2e2ef914c1d7ae942a28a8) | fix | incorrect spans for template literals ([#60323](https://github.com/angular/angular/pull/60323)) ([#60331](https://github.com/angular/angular/pull/60331)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [23ca88522b](https://github.com/angular/angular/commit/23ca88522bbc23e24a2b20e48e62edcce3a42eb6) | fix | handle transformed classes when generating HMR code ([#60298](https://github.com/angular/angular/pull/60298)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6dc41265fd](https://github.com/angular/angular/commit/6dc41265fd47df3fd0462bcd8f451eb6aea972ef) | fix | check whether application is destroyed before initializing event replay ([#59789](https://github.com/angular/angular/pull/59789)) |
|
||||
| [bb12b30d52](https://github.com/angular/angular/commit/bb12b30d5213912f50f53aff60a11e6d47349c82) | fix | ensures immediate trigger fires properly with lazy loaded routes ([#60203](https://github.com/angular/angular/pull/60203)) |
|
||||
| [b144dd946e](https://github.com/angular/angular/commit/b144dd946e38e52ce716aca7c6ba7c1a1a02f13d) | fix | fix removal of a container reference used in the component file ([#60210](https://github.com/angular/angular/pull/60210)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [15c42969fc](https://github.com/angular/angular/commit/15c42969fc42c76e3bd593201164183fb82d70f6) | fix | add missing peer dependency for `rxjs` ([#60308](https://github.com/angular/angular/pull/60308)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [7bcdf7c143](https://github.com/angular/angular/commit/7bcdf7c1435f766b2b0bbd383358ef2ddabf217a) | fix | update symbols ([#60233](https://github.com/angular/angular/pull/60233)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.1"></a>
|
||||
# 19.2.1 (2025-03-05)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [c2de5f68b3](https://github.com/angular/angular/commit/c2de5f68b3d3cd8a0c43b3c4325a1a1db874e132) | fix | clean up `onUrlChange` listener when root scope is destroyed ([#60004](https://github.com/angular/angular/pull/60004)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1dd94476b3](https://github.com/angular/angular/commit/1dd94476b35d08e753766b1d0c5d8af5faa017a9) | fix | ensure template IDs are not reused if a source file changes ([#60152](https://github.com/angular/angular/pull/60152)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1b3b05bf72](https://github.com/angular/angular/commit/1b3b05bf7294963aa50f83b726db620b250b1a5c) | fix | cache ComponentRef inputs and outputs ([#60156](https://github.com/angular/angular/pull/60156)) |
|
||||
| [330c24aed9](https://github.com/angular/angular/commit/330c24aed92e4b916fb7ac6a91fb17c3ae728869) | fix | prevent invoking replay listeners on disconnected nodes ([#60103](https://github.com/angular/angular/pull/60103)) |
|
||||
| [cfad089cc3](https://github.com/angular/angular/commit/cfad089cc359cddf08d462662d2a6bf3acab9b22) | fix | prevents event replay from being called on comment nodes ([#60130](https://github.com/angular/angular/pull/60130)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [3f0116607d](https://github.com/angular/angular/commit/3f0116607dc3ad7e31cb4d895a56094f77c82f5d) | fix | Forward the tags for quick info from the type definition ([#59524](https://github.com/angular/angular/pull/59524)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.0"></a>
|
||||
# 19.2.0 (2025-02-26)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [3e39da593a](https://github.com/angular/angular/commit/3e39da593a0a0c047a2a03b8d5fcabf9dbace40f) | feat | introduce experimental `httpResource` ([#59876](https://github.com/angular/angular/pull/59876)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5b20bab96d](https://github.com/angular/angular/commit/5b20bab96d20fe89b5cc4b4af28edbaae2604da1) | feat | Add Skip Hydration diagnostic. ([#59576](https://github.com/angular/angular/pull/59576)) |
|
||||
| [fe8a68329b](https://github.com/angular/angular/commit/fe8a68329b50363f914a728579392f3fc68670a6) | feat | support untagged template literals in expressions ([#59230](https://github.com/angular/angular/pull/59230)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [2588985f43](https://github.com/angular/angular/commit/2588985f433b20a6a5a8d239347291f5d6fb2451) | feat | pass signal node to throwInvalidWriteToSignalErrorFn ([#59600](https://github.com/angular/angular/pull/59600)) |
|
||||
| [168516462a](https://github.com/angular/angular/commit/168516462a9673b158fcaa38b8ce17bf684a8ac9) | feat | support default value in `resource()` ([#59655](https://github.com/angular/angular/pull/59655)) |
|
||||
| [bc2ad7bfd3](https://github.com/angular/angular/commit/bc2ad7bfd37a61992b550943de5da0eab2eec98b) | feat | support streaming resources ([#59573](https://github.com/angular/angular/pull/59573)) |
|
||||
| [146ab9a76e](https://github.com/angular/angular/commit/146ab9a76e6b4d8db7d08d34e2571ba5207f8756) | feat | support TypeScript 5.8 ([#59830](https://github.com/angular/angular/pull/59830)) |
|
||||
| [6c92d65349](https://github.com/angular/angular/commit/6c92d653493404a5f13aa59cde390bcbed973fb6) | fix | add `hasValue` narrowing to `ResourceRef` ([#59708](https://github.com/angular/angular/pull/59708)) |
|
||||
| [96e602ebe9](https://github.com/angular/angular/commit/96e602ebe9cdf7355befad22c11f9f91e0436e01) | fix | cancel in-progress request when same value is assigned ([#59280](https://github.com/angular/angular/pull/59280)) |
|
||||
| [6789c7ef94](https://github.com/angular/angular/commit/6789c7ef947952551d7598fe37a3d86093b75720) | fix | Defer afterRender until after first CD ([#59455](https://github.com/angular/angular/pull/59455)) ([#59551](https://github.com/angular/angular/pull/59551)) |
|
||||
| [c87e581dd9](https://github.com/angular/angular/commit/c87e581dd9e240c88cea50f222942873bdccd01d) | fix | Don't run effects in check no changes pass ([#59455](https://github.com/angular/angular/pull/59455)) ([#59551](https://github.com/angular/angular/pull/59551)) |
|
||||
| [127fc0dc84](https://github.com/angular/angular/commit/127fc0dc847a4e8b62be36cdd980a067c4da974f) | fix | fix `resource()`'s `previous.state` ([#59708](https://github.com/angular/angular/pull/59708)) |
|
||||
| [b592b1b051](https://github.com/angular/angular/commit/b592b1b0516786c52c7d0638c4e7545b0de8a545) | fix | fix race condition in resource() ([#59851](https://github.com/angular/angular/pull/59851)) |
|
||||
| [a299e02e91](https://github.com/angular/angular/commit/a299e02e9141cdc4d74185deb58308fa010bb36e) | fix | preserve tracing snapshot until tick finishes ([#59796](https://github.com/angular/angular/pull/59796)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fa0c3e3210](https://github.com/angular/angular/commit/fa0c3e3210885a36e5c9e9eb76e821032f5cd215) | feat | support type set in form validators ([#45793](https://github.com/angular/angular/pull/45793)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1cd3a7db83](https://github.com/angular/angular/commit/1cd3a7db83e1d05a31d23324676420b614cdabe2) | feat | add migration to convert templates to use self-closing tags ([#57342](https://github.com/angular/angular/pull/57342)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e6cb411e43](https://github.com/angular/angular/commit/e6cb411e4393a4b1f5852d3d7c5b9622504399b1) | fix | automatically disable animations on the server ([#59762](https://github.com/angular/angular/pull/59762)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fc5d187da5](https://github.com/angular/angular/commit/fc5d187da5e8895d60caa35b7b59e234998eddf0) | fix | decouple server from animations module ([#59762](https://github.com/angular/angular/pull/59762)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.1.7"></a>
|
||||
# 19.1.7 (2025-02-19)
|
||||
### common
|
||||
@@ -15,30 +250,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.0-next.3"></a>
|
||||
# 19.2.0-next.3 (2025-02-13)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9e847fc60d](https://github.com/angular/angular/commit/9e847fc60d4eef47e665e789ccd2d4f0b4bb94ea) | fix | handle tracking expressions requiring temporary variables ([#58520](https://github.com/angular/angular/pull/58520)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5cd26a9420](https://github.com/angular/angular/commit/5cd26a94206dfe8aabdf0dd15bfc09e7a8c606da) | fix | handle deferred blocks with shared dependencies correctly ([#59926](https://github.com/angular/angular/pull/59926)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6789c7ef94](https://github.com/angular/angular/commit/6789c7ef947952551d7598fe37a3d86093b75720) | fix | Defer afterRender until after first CD ([#59455](https://github.com/angular/angular/pull/59455)) ([#59551](https://github.com/angular/angular/pull/59551)) |
|
||||
| [c87e581dd9](https://github.com/angular/angular/commit/c87e581dd9e240c88cea50f222942873bdccd01d) | fix | Don't run effects in check no changes pass ([#59455](https://github.com/angular/angular/pull/59455)) ([#59551](https://github.com/angular/angular/pull/59551)) |
|
||||
| [b0266bda4a](https://github.com/angular/angular/commit/b0266bda4ad4efd19710fd0363a50984f48269dc) | fix | invalidate HMR component if replacement throws an error ([#59854](https://github.com/angular/angular/pull/59854)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [aa285c548c](https://github.com/angular/angular/commit/aa285c548c164917ebb5760802484843c6830daf) | fix | account for let declarations in control flow migration ([#59861](https://github.com/angular/angular/pull/59861)) |
|
||||
| [1119f85ca9](https://github.com/angular/angular/commit/1119f85ca935f43641f53ee8bc50efa47ad09717) | fix | count used dependencies inside existing control flow ([#59861](https://github.com/angular/angular/pull/59861)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.1.6"></a>
|
||||
# 19.1.6 (2025-02-12)
|
||||
### compiler
|
||||
@@ -61,27 +272,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.0-next.2"></a>
|
||||
# 19.2.0-next.2 (2025-02-06)
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a97136052f9](https://github.com/angular/angular/commit/a97136052f9cfdb696a3b335ed7a1b9fb994d408) | fix | gracefully fall back if const enum cannot be passed through ([#59815](https://github.com/angular/angular/pull/59815)) |
|
||||
| [bae94b82fda](https://github.com/angular/angular/commit/bae94b82fda1669a6aafad975a3a0eb6f2743039) | fix | handle const enums used inside HMR data ([#59815](https://github.com/angular/angular/pull/59815)) |
|
||||
| [03bcd30e348](https://github.com/angular/angular/commit/03bcd30e3487443983d450a5dfea2d68d551bbd3) | fix | handle enum members without initializers in partial evaluator ([#59815](https://github.com/angular/angular/pull/59815)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [146ab9a76e6](https://github.com/angular/angular/commit/146ab9a76e6b4d8db7d08d34e2571ba5207f8756) | feat | support TypeScript 5.8 ([#59830](https://github.com/angular/angular/pull/59830)) |
|
||||
| [b592b1b0516](https://github.com/angular/angular/commit/b592b1b0516786c52c7d0638c4e7545b0de8a545) | fix | fix race condition in resource() ([#59851](https://github.com/angular/angular/pull/59851)) |
|
||||
| [a299e02e914](https://github.com/angular/angular/commit/a299e02e9141cdc4d74185deb58308fa010bb36e) | fix | preserve tracing snapshot until tick finishes ([#59796](https://github.com/angular/angular/pull/59796)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fa0c3e32108](https://github.com/angular/angular/commit/fa0c3e3210885a36e5c9e9eb76e821032f5cd215) | feat | support type set in form validators ([#45793](https://github.com/angular/angular/pull/45793)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.1.5"></a>
|
||||
# 19.1.5 (2025-02-06)
|
||||
### compiler-cli
|
||||
@@ -93,45 +283,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.0-next.1"></a>
|
||||
# 19.2.0-next.1 (2025-01-29)
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [168516462a9](https://github.com/angular/angular/commit/168516462a9673b158fcaa38b8ce17bf684a8ac9) | feat | support default value in `resource()` ([#59655](https://github.com/angular/angular/pull/59655)) |
|
||||
| [6c92d653493](https://github.com/angular/angular/commit/6c92d653493404a5f13aa59cde390bcbed973fb6) | fix | add `hasValue` narrowing to `ResourceRef` ([#59708](https://github.com/angular/angular/pull/59708)) |
|
||||
| [96e602ebe9c](https://github.com/angular/angular/commit/96e602ebe9cdf7355befad22c11f9f91e0436e01) | fix | cancel in-progress request when same value is assigned ([#59280](https://github.com/angular/angular/pull/59280)) |
|
||||
| [cf9054248d1](https://github.com/angular/angular/commit/cf9054248d1b8b5658c5f8b582cea71535f081c2) | fix | check whether application is destroyed before printing hydration stats ([#59716](https://github.com/angular/angular/pull/59716)) |
|
||||
| [82876242e55](https://github.com/angular/angular/commit/82876242e557abbced793cff06c4d68c4721e6d2) | fix | ensure type is preserved during HMR ([#59700](https://github.com/angular/angular/pull/59700)) |
|
||||
| [127fc0dc847](https://github.com/angular/angular/commit/127fc0dc847a4e8b62be36cdd980a067c4da974f) | fix | fix `resource()`'s `previous.state` ([#59708](https://github.com/angular/angular/pull/59708)) |
|
||||
| [f862ace1819](https://github.com/angular/angular/commit/f862ace18191d7fda3b12fc3c6486c035a6b431d) | fix | fixes test timer-based test flakiness in CI ([#59674](https://github.com/angular/angular/pull/59674)) |
|
||||
### elements
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0c1ecb453c8](https://github.com/angular/angular/commit/0c1ecb453c8b01447fb5a9f38fdfe9936a60abe5) | fix | not setting initial value on signal-based input ([#59773](https://github.com/angular/angular/pull/59773)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e6cb411e439](https://github.com/angular/angular/commit/e6cb411e4393a4b1f5852d3d7c5b9622504399b1) | fix | automatically disable animations on the server ([#59762](https://github.com/angular/angular/pull/59762)) |
|
||||
| [6b09716754b](https://github.com/angular/angular/commit/6b09716754b979c98489a2710eda977e51fe92d0) | fix | prepend `baseHref` to `sourceMappingURL` in CSS content ([#59730](https://github.com/angular/angular/pull/59730)) |
|
||||
| [fd7ee47bf70](https://github.com/angular/angular/commit/fd7ee47bf702029e481ad40362684f0a01ef2ec0) | fix | Update pseudoevent created by createMouseSpecialEvent to populate `_originalEvent` property ([#59690](https://github.com/angular/angular/pull/59690)) |
|
||||
| [65cf061feb4](https://github.com/angular/angular/commit/65cf061feb402e1532cd06d0fda5d2b7749ac421) | fix | Update pseudoevent created by createMouseSpecialEvent to populate `_originalEvent` property ([#59690](https://github.com/angular/angular/pull/59690)) |
|
||||
| [af9d74710c4](https://github.com/angular/angular/commit/af9d74710c460b0aaeaf3226533e9e544025523c) | fix | Update pseudoevent created by createMouseSpecialEvent to populate `_originalEvent` property ([#59690](https://github.com/angular/angular/pull/59690)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fc5d187da5e](https://github.com/angular/angular/commit/fc5d187da5e8895d60caa35b7b59e234998eddf0) | fix | decouple server from animations module ([#59762](https://github.com/angular/angular/pull/59762)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [c7b6e1107c6](https://github.com/angular/angular/commit/c7b6e1107c6fcc7ed1dbb7c7b8698b09bfa8f1cc) | fix | prevent error handling when injector is destroyed ([#59457](https://github.com/angular/angular/pull/59457)) |
|
||||
### service-worker
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e2987a1d4ab](https://github.com/angular/angular/commit/e2987a1d4ab09b1abf1f42597c40f2055db116e1) | fix | add missing `rxjs` peer dependency ([#59747](https://github.com/angular/angular/pull/59747)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.1.4"></a>
|
||||
# 19.1.4 (2025-01-29)
|
||||
### core
|
||||
@@ -162,23 +313,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.2.0-next.0"></a>
|
||||
# 19.2.0-next.0 (2025-01-22)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fe8a68329b](https://github.com/angular/angular/commit/fe8a68329b50363f914a728579392f3fc68670a6) | feat | support untagged template literals in expressions ([#59230](https://github.com/angular/angular/pull/59230)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ed705a856a](https://github.com/angular/angular/commit/ed705a856a164f91d33e2173b4f371329d07c472) | feat | detect missing structural directive imports ([#59443](https://github.com/angular/angular/pull/59443)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [bc2ad7bfd3](https://github.com/angular/angular/commit/bc2ad7bfd37a61992b550943de5da0eab2eec98b) | feat | support streaming resources ([#59573](https://github.com/angular/angular/pull/59573)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="19.1.3"></a>
|
||||
# 19.1.3 (2025-01-22)
|
||||
### compiler
|
||||
|
||||
+6
-155
@@ -103,7 +103,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
|
||||
|
||||
9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
|
||||
9. Commit your changes using a descriptive commit message that follows our [commit message conventions][commit-message-guidelines].
|
||||
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||
|
||||
```shell
|
||||
@@ -145,7 +145,7 @@ That's it! Thank you for your contribution!
|
||||
|
||||
##### Updating the commit message
|
||||
|
||||
A reviewer might often suggest changes to a commit message (for example, to add more context for a change or adhere to our [commit message guidelines](#commit)).
|
||||
A reviewer might often suggest changes to a commit message (for example, to add more context for a change or adhere to our [commit message guidelines][commit-message-guidelines]).
|
||||
In order to update the commit message of the last commit on your branch:
|
||||
|
||||
1. Check out your branch:
|
||||
@@ -210,163 +210,14 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
|
||||
An automated formatter is available, see [DEVELOPER.md](contributing-docs/building-and-testing-angular.md#formatting-your-source-code).
|
||||
|
||||
|
||||
## <a name="commit"></a> Commit Message Format
|
||||
|
||||
*This specification is inspired by and supersedes the [AngularJS commit message format][commit-message-format].*
|
||||
|
||||
We have very precise rules over how our Git commit messages must be formatted.
|
||||
This format leads to **easier to read commit history**.
|
||||
|
||||
Each commit message consists of a **header**, a **body**, and a **footer**.
|
||||
|
||||
|
||||
```
|
||||
<header>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format.
|
||||
|
||||
The `body` is mandatory for all commits except for those of type "docs".
|
||||
When the body is present it must be at least 20 characters long and must conform to the [Commit Message Body](#commit-body) format.
|
||||
|
||||
The `footer` is optional. The [Commit Message Footer](#commit-footer) format describes what the footer is used for and the structure it must have.
|
||||
|
||||
|
||||
#### <a name="commit-header"></a>Commit Message Header
|
||||
## <a name="commit"></a> Commit Message Guidelines
|
||||
|
||||
We have very precise rules over how our Git commit messages must be formatted:
|
||||
```
|
||||
<type>(<scope>): <short summary>
|
||||
│ │ │
|
||||
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
|
||||
│ │
|
||||
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||
│ elements|forms|http|language-service|localize|platform-browser|
|
||||
│ platform-browser-dynamic|platform-server|router|service-worker|
|
||||
│ upgrade|zone.js|packaging|changelog|docs-infra|migrations|
|
||||
│ devtools
|
||||
│
|
||||
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test
|
||||
```
|
||||
|
||||
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
||||
|
||||
|
||||
##### Type
|
||||
|
||||
Must be one of the following:
|
||||
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (examples: Github Actions, SauceLabs)
|
||||
* **docs**: Documentation only changes
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
* **perf**: A code change that improves performance
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
|
||||
##### Scope
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
* `animations`
|
||||
* `bazel`
|
||||
* `benchpress`
|
||||
* `common`
|
||||
* `compiler`
|
||||
* `compiler-cli`
|
||||
* `core`
|
||||
* `elements`
|
||||
* `forms`
|
||||
* `http`
|
||||
* `language-service`
|
||||
* `localize`
|
||||
* `platform-browser`
|
||||
* `platform-browser-dynamic`
|
||||
* `platform-server`
|
||||
* `router`
|
||||
* `service-worker`
|
||||
* `upgrade`
|
||||
* `zone.js`
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
||||
* `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||
|
||||
* `changelog`: used for updating the release notes in CHANGELOG.md
|
||||
|
||||
* `dev-infra`: used for dev-infra related changes within the directories /scripts and /tools
|
||||
|
||||
* `docs-infra`: used for docs-app (angular.dev) related changes within the /adev directory of the repo
|
||||
|
||||
* `migrations`: used for changes to the `ng update` migrations.
|
||||
|
||||
* `devtools`: used for changes in the [browser extension](./devtools/README.md).
|
||||
|
||||
* none/empty string: useful for `test` and `refactor` changes that are done across all packages (e.g. `test: add missing unit tests`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
|
||||
|
||||
|
||||
##### Summary
|
||||
|
||||
Use the summary field to provide a succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize the first letter
|
||||
* no dot (.) at the end
|
||||
|
||||
|
||||
#### <a name="commit-body"></a>Commit Message Body
|
||||
|
||||
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
|
||||
|
||||
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
|
||||
You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
|
||||
|
||||
|
||||
#### <a name="commit-footer"></a>Commit Message Footer
|
||||
|
||||
The footer can contain information about breaking changes and deprecations and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
|
||||
For example:
|
||||
|
||||
```
|
||||
BREAKING CHANGE: <breaking change summary>
|
||||
<BLANK LINE>
|
||||
<breaking change description + migration instructions>
|
||||
<BLANK LINE>
|
||||
<BLANK LINE>
|
||||
Fixes #<issue number>
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
DEPRECATED: <what is deprecated>
|
||||
<BLANK LINE>
|
||||
<deprecation description + recommended update path>
|
||||
<BLANK LINE>
|
||||
<BLANK LINE>
|
||||
Closes #<pr number>
|
||||
```
|
||||
|
||||
Breaking Change section should start with the phrase `BREAKING CHANGE: ` followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
|
||||
|
||||
Similarly, a Deprecation section should start with `DEPRECATED: ` followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path.
|
||||
|
||||
|
||||
### Revert commits
|
||||
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
|
||||
|
||||
The content of the commit message body should contain:
|
||||
|
||||
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
|
||||
- a clear description of the reason for reverting the commit message.
|
||||
|
||||
See [Commit Message Guidelines][commit-message-guidelines] for details.
|
||||
|
||||
## <a name="cla"></a> Signing the CLA
|
||||
|
||||
@@ -390,9 +241,9 @@ The following documents can help you sort out issues with GitHub accounts and mu
|
||||
|
||||
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/main/CODE_OF_CONDUCT.md
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[corporate-cla]: https://cla.developers.google.com/about/google-corporate
|
||||
[dev-doc]: ./contributing-docs/building-and-testing-angular.md
|
||||
[commit-message-guidelines]: ./contributing-docs/commit-message-guidelines.md
|
||||
[github]: https://github.com/angular/angular
|
||||
[discord]: https://discord.gg/angular
|
||||
[individual-cla]: https://cla.developers.google.com/about/google-individual
|
||||
|
||||
@@ -143,10 +143,10 @@ cldr_xml_data_repository(
|
||||
# sass rules
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "54bca211ea0a4de2c740a6e24b9d11225942a95452799b6a64dba36e082b7249",
|
||||
strip_prefix = "rules_sass-c01e8848f30f8e4672babcbe41c3ac3551f3a800",
|
||||
sha256 = "bff856619317a388292970a7d4bfea8c9e627a1886fe7132075d378d4067c09e",
|
||||
strip_prefix = "rules_sass-cbe5261f925751a465a1a54bf2147e5f696ec567",
|
||||
urls = [
|
||||
"https://github.com/bazelbuild/rules_sass/archive/c01e8848f30f8e4672babcbe41c3ac3551f3a800.zip",
|
||||
"https://github.com/bazelbuild/rules_sass/archive/cbe5261f925751a465a1a54bf2147e5f696ec567.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+3
-1
@@ -23,7 +23,9 @@ APPLICATION_FILES = [
|
||||
exclude = [
|
||||
"src/**/*.spec.ts",
|
||||
],
|
||||
)
|
||||
) + [
|
||||
"//adev/src/content/examples:embeddable",
|
||||
]
|
||||
|
||||
TEST_FILES = APPLICATION_FILES + [
|
||||
"karma.conf.js",
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ cd angular
|
||||
yarn
|
||||
|
||||
# Build and run local dev server
|
||||
# Note: Initial build will take some time
|
||||
# NOTE: Initial build will take some time
|
||||
yarn docs
|
||||
```
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ ng_module(
|
||||
module_name = "@angular/docs",
|
||||
deps = [
|
||||
"//adev/shared-docs/components",
|
||||
"//adev/shared-docs/constants",
|
||||
"//adev/shared-docs/directives",
|
||||
"//adev/shared-docs/interfaces",
|
||||
"//adev/shared-docs/pipes",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
|
||||
load("//tools:defaults.bzl", "karma_web_test_suite", "ng_module", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
@@ -23,6 +23,7 @@ ng_module(
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/router",
|
||||
"@npm//@angular/material",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -7,8 +7,15 @@
|
||||
<li class="docs-faceted-list-item">
|
||||
@if (item.path) {
|
||||
@if (item.isExternal) {
|
||||
<a [href]="item.path" target="_blank">
|
||||
<span [class.docs-external-link]="item.isExternal">{{ item.label }}</span>
|
||||
<a
|
||||
[href]="item.path" target="_blank"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span [class.docs-external-link]="item.isExternal" class="docs-faceted-list-item-text">{{ item.label }}</span>
|
||||
@if (item.children && item.level! > 1 && !item.isExpanded) {
|
||||
<docs-icon>chevron_right</docs-icon>
|
||||
}
|
||||
@@ -25,8 +32,13 @@
|
||||
}"
|
||||
routerLinkActive="docs-faceted-list-item-active"
|
||||
(click)="emitClickOnLink()"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span>{{item.label}}</span>
|
||||
<span class="docs-faceted-list-item-text">{{item.label}}</span>
|
||||
@if (item.children && !item.isExpanded) {
|
||||
<docs-icon>chevron_right</docs-icon>
|
||||
}
|
||||
@@ -35,8 +47,14 @@
|
||||
} @else {
|
||||
<!-- Nav Section Header -->
|
||||
@if (item.level !== collapsableLevel() && item.level !== expandableLevel()) {
|
||||
<div class="docs-secondary-nav-header">
|
||||
<span>{{item.label}}</span>
|
||||
<div class="docs-secondary-nav-header"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span class="docs-faceted-list-item-text">{{item.label}}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -54,13 +72,17 @@
|
||||
[class.docs-expanded-button]="item.children && item.level == collapsableLevel()"
|
||||
[class.docs-not-expanded-button]="item.children && item.level === expandableLevel()"
|
||||
[class.docs-nav-item-has-icon]="
|
||||
item.children && item.level === expandableLevel() && !item.isExpanded
|
||||
"
|
||||
item.children && item.level === expandableLevel() && !item.isExpanded"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
@if (item.children && item.level === collapsableLevel()) {
|
||||
<docs-icon>arrow_back</docs-icon>
|
||||
}
|
||||
<span>{{ item.label }}</span>
|
||||
<span class="docs-faceted-list-item-text">{{ item.label }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.docs-faceted-list-item-text {
|
||||
max-width: calc(100% - 1rem);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.docs-nav-item-has-icon {
|
||||
&::after {
|
||||
// FIXME: for some reason this disappears when transformed
|
||||
@@ -115,7 +121,7 @@ a,
|
||||
|
||||
// Add padding-bottom to last item in the list
|
||||
.docs-navigation-list {
|
||||
width: 100%;
|
||||
width: 15rem;
|
||||
|
||||
li:last-of-type {
|
||||
ul:last-of-type {
|
||||
@@ -144,3 +150,12 @@ a,
|
||||
margin-inline-end: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .API-tooltip{
|
||||
.mat-mdc-tooltip-surface{
|
||||
max-width: unset;
|
||||
width:auto;
|
||||
word-break: keep-all;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,18 @@ import {RouterLink, RouterLinkActive} from '@angular/router';
|
||||
import {IconComponent} from '../icon/icon.component';
|
||||
import {IsActiveNavigationItem} from '../../pipes/is-active-navigation-item.pipe';
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-navigation-list',
|
||||
imports: [RouterLink, RouterLinkActive, IconComponent, IsActiveNavigationItem, NgTemplateOutlet],
|
||||
imports: [
|
||||
RouterLink,
|
||||
RouterLinkActive,
|
||||
IconComponent,
|
||||
IsActiveNavigationItem,
|
||||
NgTemplateOutlet,
|
||||
MatTooltipModule,
|
||||
],
|
||||
templateUrl: './navigation-list.component.html',
|
||||
styleUrls: ['./navigation-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
<docs-text-field
|
||||
[autofocus]="true"
|
||||
[hideIcon]="true"
|
||||
[ngModel]="searchQuery()"
|
||||
(ngModelChange)="updateSearchQuery($event)"
|
||||
[(ngModel)]="searchQuery"
|
||||
class="docs-search-input"
|
||||
placeholder="Search docs"
|
||||
/>
|
||||
|
||||
@if (searchResults() && searchResults()!.length > 0) {
|
||||
@if (searchResults.hasValue() && searchResults.value().length > 0) {
|
||||
<ul class="docs-search-results docs-mini-scroll-track">
|
||||
@for (result of searchResults(); track result.objectID) {
|
||||
@for (result of searchResults.value(); track result.id) {
|
||||
<li docsSearchItem [item]="result">
|
||||
<a
|
||||
[routerLink]="'/' + result.url | relativeLink: 'pathname'"
|
||||
@@ -22,45 +21,35 @@
|
||||
<!-- Icon -->
|
||||
<span class="docs-search-result-icon" aria-hidden="true">
|
||||
<i role="presentation" class="material-symbols-outlined docs-icon-small">
|
||||
{{ result.hierarchy.lvl0 === 'Tutorials' ? 'code' : 'description'}}
|
||||
{{ result.type === 'code' ? 'code' : 'description'}}
|
||||
</i>
|
||||
</span>
|
||||
<!-- Results type -->
|
||||
<span class="docs-search-results__type">
|
||||
@let snippet = result._snippetResult.hierarchy?.lvl1?.value ?? '';
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="highlightSnippet"
|
||||
[ngTemplateOutletContext]="{snippet}"
|
||||
/>
|
||||
</span>
|
||||
<span class="docs-search-results__type" [innerHtml]="result.labelHtml"></span>
|
||||
</div>
|
||||
|
||||
@let content = result._snippetResult.content;
|
||||
@let hierarchy = result._snippetResult.hierarchy;
|
||||
@if (content || hierarchy?.lvl2 || hierarchy?.lvl3 || hierarchy?.lvl4) {
|
||||
<span class="docs-search-results__type docs-search-results__lvl2">
|
||||
@let snippet = getBestSnippetForMatch(result);
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="highlightSnippet"
|
||||
[ngTemplateOutletContext]="{snippet}"
|
||||
/>
|
||||
@if (result.subLabelHtml) {
|
||||
<span
|
||||
class="docs-search-results__type docs-search-results__lvl2"
|
||||
[innerHtml]="result.subLabelHtml"
|
||||
>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Page title -->
|
||||
<span class="docs-result-page-title">{{ result.hierarchy?.lvl0 }}</span>
|
||||
<span class="docs-result-page-title">{{ result.category }}</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
} @else {
|
||||
<div class="docs-search-results docs-mini-scroll-track">
|
||||
@if (searchResults() === undefined) {
|
||||
@if (!searchResults.hasValue()) {
|
||||
<div class="docs-search-results__start-typing">
|
||||
<span>Start typing to see results</span>
|
||||
</div>
|
||||
} @else if (searchResults()?.length === 0) {
|
||||
} @else if (searchResults.value().length === 0) {
|
||||
<div class="docs-search-results__no-results">
|
||||
<span>No results found</span>
|
||||
</div>
|
||||
@@ -70,21 +59,13 @@
|
||||
|
||||
<div class="docs-algolia">
|
||||
<span>Search by</span>
|
||||
<a target="_blank" rel="noopener"
|
||||
href="https://www.algolia.com/developers/?utm_source=angular.dev&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://www.algolia.com/developers/?utm_source=angular.dev&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch"
|
||||
>
|
||||
<docs-algolia-icon />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<ng-template #highlightSnippet let-snippet="snippet">
|
||||
@let parts = splitMarkedText(snippet);
|
||||
@for (part of parts; track $index) {
|
||||
@if (part.highlight) {
|
||||
<mark>{{part.text}}</mark>
|
||||
} @else {
|
||||
<span>{{part.text}}</span>
|
||||
}
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
@@ -2,6 +2,8 @@ dialog {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding-block-end: 3rem;
|
||||
margin: 0 auto;
|
||||
top: 15vh;
|
||||
|
||||
&::backdrop {
|
||||
backdrop-filter: blur(5px);
|
||||
@@ -49,12 +51,17 @@ dialog {
|
||||
padding-inline-end: 1rem;
|
||||
padding-block: 0.25rem;
|
||||
|
||||
mark {
|
||||
background: #e62600;
|
||||
background: var(--red-to-orange-horizontal-gradient);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
/**
|
||||
* This rule needs ng-deep to be applied to elements that are created via a [innerHTML] binding
|
||||
*/
|
||||
::ng-deep {
|
||||
mark {
|
||||
background: #e62600;
|
||||
background: var(--red-to-orange-horizontal-gradient);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -148,8 +155,8 @@ dialog {
|
||||
border-radius: 0 0 0.25rem 0.25rem;
|
||||
|
||||
docs-algolia-icon {
|
||||
display: inline-flex;
|
||||
margin-block-start: 0.12rem;
|
||||
display: block;
|
||||
margin-block-start: 5px;
|
||||
margin-inline-start: 0.15rem;
|
||||
width: 4rem;
|
||||
}
|
||||
|
||||
@@ -9,54 +9,59 @@
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {SearchDialog} from './search-dialog.component';
|
||||
import {WINDOW} from '../../providers';
|
||||
import {Search} from '../../services';
|
||||
import {ENVIRONMENT, WINDOW} from '../../providers';
|
||||
import {ALGOLIA_CLIENT, Search} from '../../services';
|
||||
import {FakeEventTarget} from '../../testing/index';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {Router} from '@angular/router';
|
||||
import {provideExperimentalZonelessChangeDetection} from '@angular/core';
|
||||
import {
|
||||
ApplicationRef,
|
||||
provideExperimentalZonelessChangeDetection,
|
||||
ResourceStatus,
|
||||
} from '@angular/core';
|
||||
import {SearchResult} from '../../interfaces';
|
||||
|
||||
describe('SearchDialog', () => {
|
||||
let fixture: ComponentFixture<SearchDialog>;
|
||||
|
||||
const fakeSearch = {
|
||||
searchQuery: jasmine.createSpy(),
|
||||
searchResults: jasmine.createSpy(),
|
||||
};
|
||||
const searchResults = jasmine.createSpy();
|
||||
|
||||
const fakeWindow = new FakeEventTarget();
|
||||
|
||||
beforeEach(async () => {
|
||||
fakeSearch.searchResults.and.returnValue([]);
|
||||
fakeSearch.searchQuery.and.returnValue('');
|
||||
let search: Search;
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
beforeEach(async () => {
|
||||
searchResults.and.returnValue([]);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [SearchDialog, RouterTestingModule],
|
||||
providers: [
|
||||
provideExperimentalZonelessChangeDetection(),
|
||||
{
|
||||
provide: Search,
|
||||
useValue: fakeSearch,
|
||||
},
|
||||
{
|
||||
provide: WINDOW,
|
||||
useValue: fakeWindow,
|
||||
},
|
||||
{provide: ENVIRONMENT, useValue: {algolia: {index: 'fakeIndex'}}},
|
||||
{provide: ALGOLIA_CLIENT, useValue: {search: searchResults}},
|
||||
{provide: WINDOW, useValue: fakeWindow},
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(SearchDialog);
|
||||
fixture.detectChanges();
|
||||
search = TestBed.inject(Search);
|
||||
});
|
||||
|
||||
it('should navigate to active item when user pressed Enter', () => {
|
||||
it('should navigate to active item when user pressed Enter', async () => {
|
||||
const router = TestBed.inject(Router);
|
||||
const navigateByUrlSpy = spyOn(router, 'navigateByUrl');
|
||||
|
||||
fakeSearch.searchResults.and.returnValue(fakeSearchResults);
|
||||
fixture.detectChanges();
|
||||
search.searchQuery.set('fakeQuery');
|
||||
searchResults.and.returnValue(Promise.resolve({results: [{hits: fakeSearchResults}]}));
|
||||
|
||||
// Fire the request
|
||||
TestBed.inject(ApplicationRef).tick();
|
||||
|
||||
// Wait for the resource to resolve
|
||||
await TestBed.inject(ApplicationRef).whenStable();
|
||||
|
||||
fakeWindow.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
@@ -78,9 +83,15 @@ describe('SearchDialog', () => {
|
||||
expect(algoliaIcon).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display `No results found` message when there are no results for provided query', () => {
|
||||
fakeSearch.searchResults.and.returnValue([]);
|
||||
fixture.detectChanges();
|
||||
it('should display `No results found` message when there are no results for provided query', async () => {
|
||||
search.searchQuery.set('fakeQuery');
|
||||
searchResults.and.returnValue(Promise.resolve({results: [{hits: []}]}));
|
||||
|
||||
// Fire the request
|
||||
TestBed.inject(ApplicationRef).tick();
|
||||
|
||||
// Wait for the resource to resolve
|
||||
await TestBed.inject(ApplicationRef).whenStable();
|
||||
|
||||
const noResultsContainer = fixture.debugElement.query(
|
||||
By.css('.docs-search-results__no-results'),
|
||||
@@ -90,7 +101,7 @@ describe('SearchDialog', () => {
|
||||
});
|
||||
|
||||
it('should display `Start typing to see results` message when there are no provided query', () => {
|
||||
fakeSearch.searchResults.and.returnValue(undefined);
|
||||
searchResults.and.returnValue(undefined);
|
||||
fixture.detectChanges();
|
||||
|
||||
const startTypingContainer = fixture.debugElement.query(
|
||||
@@ -100,9 +111,15 @@ describe('SearchDialog', () => {
|
||||
expect(startTypingContainer).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display list of the search results when results exist', () => {
|
||||
fakeSearch.searchResults.and.returnValue(fakeSearchResults);
|
||||
fixture.detectChanges();
|
||||
it('should display list of the search results when results exist', async () => {
|
||||
search.searchQuery.set('fakeQuery');
|
||||
searchResults.and.returnValue(Promise.resolve({results: [{hits: fakeSearchResults}]}));
|
||||
|
||||
// Fire the request
|
||||
TestBed.inject(ApplicationRef).tick();
|
||||
|
||||
// Wait for the resource to resolve
|
||||
await TestBed.inject(ApplicationRef).whenStable();
|
||||
|
||||
const resultListContainer = fixture.debugElement.query(By.css('ul.docs-search-results'));
|
||||
const resultItems = fixture.debugElement.queryAll(By.css('ul.docs-search-results li a'));
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ElementRef,
|
||||
Injector,
|
||||
OnDestroy,
|
||||
Signal,
|
||||
afterNextRender,
|
||||
effect,
|
||||
inject,
|
||||
@@ -20,7 +19,6 @@ import {
|
||||
viewChild,
|
||||
viewChildren,
|
||||
} from '@angular/core';
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
|
||||
import {WINDOW} from '../../providers/index';
|
||||
import {ClickOutside} from '../../directives/index';
|
||||
@@ -32,10 +30,9 @@ import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
|
||||
import {SearchItem} from '../../directives/search-item/search-item.directive';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {filter, fromEvent} from 'rxjs';
|
||||
import {fromEvent} from 'rxjs';
|
||||
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
|
||||
import {RelativeLink} from '../../pipes/relative-link.pipe';
|
||||
import {SearchResult, SnippetResult} from '../../interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-search-dialog',
|
||||
@@ -48,7 +45,6 @@ import {SearchResult, SnippetResult} from '../../interfaces';
|
||||
AlgoliaIcon,
|
||||
RelativeLink,
|
||||
RouterLink,
|
||||
NgTemplateOutlet,
|
||||
],
|
||||
templateUrl: './search-dialog.component.html',
|
||||
styleUrls: ['./search-dialog.component.scss'],
|
||||
@@ -106,46 +102,6 @@ export class SearchDialog implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
splitMarkedText(snippet: string): Array<{highlight: boolean; text: string}> {
|
||||
const parts: Array<{highlight: boolean; text: string}> = [];
|
||||
while (snippet.indexOf('<ɵ>') !== -1) {
|
||||
const beforeMatch = snippet.substring(0, snippet.indexOf('<ɵ>'));
|
||||
const match = snippet.substring(snippet.indexOf('<ɵ>') + 3, snippet.indexOf('</ɵ>'));
|
||||
parts.push({highlight: false, text: beforeMatch});
|
||||
parts.push({highlight: true, text: match});
|
||||
snippet = snippet.substring(snippet.indexOf('</ɵ>') + 4);
|
||||
}
|
||||
parts.push({highlight: false, text: snippet});
|
||||
return parts;
|
||||
}
|
||||
|
||||
getBestSnippetForMatch(result: SearchResult): string {
|
||||
// if there is content, return it
|
||||
if (result._snippetResult.content !== undefined) {
|
||||
return result._snippetResult.content.value;
|
||||
}
|
||||
|
||||
const hierarchy = result._snippetResult.hierarchy;
|
||||
if (hierarchy === undefined) {
|
||||
return '';
|
||||
}
|
||||
function matched(snippet: SnippetResult | undefined) {
|
||||
return snippet?.matchLevel !== undefined && snippet.matchLevel !== 'none';
|
||||
}
|
||||
// return the most specific subheader match
|
||||
if (matched(hierarchy.lvl4)) {
|
||||
return hierarchy.lvl4!.value;
|
||||
}
|
||||
if (matched(hierarchy.lvl3)) {
|
||||
return hierarchy.lvl3!.value;
|
||||
}
|
||||
if (matched(hierarchy.lvl2)) {
|
||||
return hierarchy.lvl2!.value;
|
||||
}
|
||||
// if no subheader matched the query, fall back to just returning the most specific one
|
||||
return hierarchy.lvl3?.value ?? hierarchy.lvl2?.value ?? '';
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.keyManager.destroy();
|
||||
}
|
||||
@@ -155,10 +111,6 @@ export class SearchDialog implements OnDestroy {
|
||||
this.onClose.emit();
|
||||
}
|
||||
|
||||
updateSearchQuery(query: string) {
|
||||
this.search.updateSearchQuery(query);
|
||||
}
|
||||
|
||||
private navigateToTheActiveItem(): void {
|
||||
const activeItemLink: string | undefined = this.keyManager.activeItem?.item?.url;
|
||||
|
||||
|
||||
@@ -6,26 +6,18 @@
|
||||
<ul class="docs-faceted-list">
|
||||
<!-- TODO: Hide li elements with class docs-toc-item-h3 for laptop, table and phone screen resolutions -->
|
||||
@for (item of tableOfContentItems(); track item.id) {
|
||||
<li
|
||||
class="docs-faceted-list-item"
|
||||
[class.docs-toc-item-h2]="item.level === TableOfContentsLevel.H2"
|
||||
[class.docs-toc-item-h3]="item.level === TableOfContentsLevel.H3"
|
||||
>
|
||||
<!-- Not using routerLink + fragment because of: https://github.com/angular/angular/issues/30139 -->
|
||||
<a
|
||||
[href]="location.path() + '#' + item.id"
|
||||
[class.docs-faceted-list-item-active]="item.id === activeItemId()"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="docs-faceted-list-item" [class.docs-toc-item-h2]="item.level === TableOfContentsLevel.H2"
|
||||
[class.docs-toc-item-h3]="item.level === TableOfContentsLevel.H3">
|
||||
<!-- Not using routerLink + fragment because of: https://github.com/angular/angular/issues/30139 -->
|
||||
<a [href]="location.path() + '#' + item.id" [class.docs-faceted-list-item-active]="item.id === activeItemId()">
|
||||
{{ item.title }}
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
@if (shouldDisplayScrollToTop()) {
|
||||
<button type="button" (click)="scrollToTop()">
|
||||
<docs-icon role="presentation">arrow_upward_alt</docs-icon>
|
||||
Back to the top
|
||||
</button>
|
||||
}
|
||||
</aside>
|
||||
<button type="button" (click)="scrollToTop()">
|
||||
<docs-icon role="presentation">arrow_upward_alt</docs-icon>
|
||||
Back to the top
|
||||
</button>
|
||||
</aside>
|
||||
@@ -9,6 +9,7 @@
|
||||
padding-inline: 1rem;
|
||||
max-height: 100vh;
|
||||
overflow-y: scroll;
|
||||
box-sizing: border-box;
|
||||
|
||||
aside {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
Component,
|
||||
DestroyRef,
|
||||
input,
|
||||
computed,
|
||||
inject,
|
||||
afterNextRender,
|
||||
} from '@angular/core';
|
||||
import {Location} from '@angular/common';
|
||||
import {TableOfContentsLevel} from '../../interfaces/index';
|
||||
@@ -39,12 +39,15 @@ export class TableOfContents {
|
||||
tableOfContentItems = this.tableOfContentsLoader.tableOfContentItems;
|
||||
|
||||
activeItemId = this.scrollSpy.activeItemId;
|
||||
shouldDisplayScrollToTop = computed(() => !this.scrollSpy.scrollbarThumbOnTop());
|
||||
TableOfContentsLevel = TableOfContentsLevel;
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.tableOfContentsLoader.buildTableOfContent(this.contentSourceElement());
|
||||
this.scrollSpy.startListeningToScroll(this.contentSourceElement(), this.destroyRef);
|
||||
constructor() {
|
||||
afterNextRender({
|
||||
read: () => {
|
||||
this.tableOfContentsLoader.buildTableOfContent(this.contentSourceElement());
|
||||
this.scrollSpy.setupActiveItemListener(this.contentSourceElement(), this.destroyRef);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
scrollToTop(): void {
|
||||
|
||||
@@ -121,7 +121,7 @@ describe('DocViewer', () => {
|
||||
expect(checkIcon.nativeElement.innerHTML).toBe('check');
|
||||
});
|
||||
|
||||
it('should display example viewer in multi file mode when user clicks expand', async () => {
|
||||
it('should display example viewer in multi file mode when provided example is multi file snippet', async () => {
|
||||
const fixture = TestBed.createComponent(DocViewer);
|
||||
fixture.componentRef.setInput(
|
||||
'docContent',
|
||||
@@ -131,10 +131,6 @@ describe('DocViewer', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
const exampleViewer = fixture.debugElement.query(By.directive(ExampleViewer));
|
||||
const expandButton = fixture.debugElement.query(
|
||||
By.css('button[aria-label="Expand code example"]'),
|
||||
);
|
||||
expandButton.nativeElement.click();
|
||||
|
||||
expect(exampleViewer).not.toBeNull();
|
||||
expect(exampleViewer.componentInstance.view()).toBe(CodeExampleViewMode.MULTI_FILE);
|
||||
|
||||
@@ -196,13 +196,13 @@ export class DocViewer implements OnChanges {
|
||||
const exampleRef = this.viewContainer.createComponent(ExampleViewer);
|
||||
|
||||
this.countOfExamples++;
|
||||
exampleRef.instance.metadata = {
|
||||
exampleRef.setInput('metadata', {
|
||||
title: title ?? firstCodeSnippetTitle,
|
||||
path,
|
||||
files: snippets,
|
||||
preview,
|
||||
id: this.countOfExamples,
|
||||
};
|
||||
});
|
||||
|
||||
exampleRef.instance.githubUrl = `${GITHUB_CONTENT_URL}/${snippets[0].name}`;
|
||||
exampleRef.instance.stackblitzUrl = `${ASSETS_EXAMPLES_PATH}/${snippets[0].name}.html`;
|
||||
@@ -293,9 +293,6 @@ export class DocViewer implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger change detection after setting inputs.
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
// Attach a view to the ApplicationRef for change detection
|
||||
// purposes and for hydration serialization to pick it up
|
||||
// during SSG.
|
||||
|
||||
+42
-9
@@ -100,26 +100,59 @@ describe('ExampleViewer', () => {
|
||||
expect(component.tabs()![2].name).toBe('another-example.ts');
|
||||
});
|
||||
|
||||
it('should expandable be false when none of the example files have defined visibleLinesRange ', waitForAsync(async () => {
|
||||
it('should expand button not appear when there is no hidden line', waitForAsync(async () => {
|
||||
component.metadata = getMetadata();
|
||||
await component.renderExample();
|
||||
expect(component.expandable()).toBeFalse();
|
||||
const button = fixture.debugElement.query(By.css('button[aria-label="Expand code example"]'));
|
||||
expect(button).toBeNull();
|
||||
}));
|
||||
|
||||
it('should expandable be true when at least one example file has defined visibleLinesRange ', waitForAsync(async () => {
|
||||
it('should have line with hidden line class when expand button is present', waitForAsync(async () => {
|
||||
const expectedCodeSnippetContent =
|
||||
'typescript code<br/>' + '<div class="line">hidden line</div>';
|
||||
|
||||
component.metadata = getMetadata({
|
||||
files: [
|
||||
{name: 'example.ts', content: 'typescript file'},
|
||||
{
|
||||
name: 'example.html',
|
||||
content: 'html file',
|
||||
visibleLinesRange: '[1, 2]',
|
||||
name: 'example.ts',
|
||||
content: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
visibleLinesRange: '[1]',
|
||||
},
|
||||
{name: 'another-example.ts', content: 'css file'},
|
||||
],
|
||||
});
|
||||
|
||||
await component.renderExample();
|
||||
expect(component.expandable()).toBeTrue();
|
||||
fixture.detectChanges();
|
||||
|
||||
const hiddenLine = fixture.debugElement.query(By.css('div[class="line hidden"]'));
|
||||
expect(hiddenLine).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should have no more line with hidden line class when expand button is clicked', waitForAsync(async () => {
|
||||
const expectedCodeSnippetContent =
|
||||
'typescript code<br/>' + '<div class="line">hidden line</div>';
|
||||
|
||||
component.metadata = getMetadata({
|
||||
files: [
|
||||
{
|
||||
name: 'example.ts',
|
||||
content: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
visibleLinesRange: '[1]',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await component.renderExample();
|
||||
fixture.detectChanges();
|
||||
|
||||
const expandButton = fixture.debugElement.query(
|
||||
By.css('button[aria-label="Expand code example"]'),
|
||||
);
|
||||
expandButton.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const hiddenLine = fixture.debugElement.query(By.css('div[class="line hidden"]'));
|
||||
expect(hiddenLine).toBeNull();
|
||||
}));
|
||||
|
||||
it('should set exampleComponent when metadata contains path and preview is true', waitForAsync(async () => {
|
||||
|
||||
@@ -7,18 +7,21 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
DestroyRef,
|
||||
Input,
|
||||
Type,
|
||||
computed,
|
||||
inject,
|
||||
ChangeDetectorRef,
|
||||
ViewChild,
|
||||
signal,
|
||||
Component,
|
||||
computed,
|
||||
DestroyRef,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
inject,
|
||||
Injector,
|
||||
input,
|
||||
Input,
|
||||
signal,
|
||||
Type,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import {CommonModule, DOCUMENT} from '@angular/common';
|
||||
import {MatTabGroup, MatTabsModule} from '@angular/material/tabs';
|
||||
@@ -47,19 +50,17 @@ export const HIDDEN_CLASS_NAME = 'hidden';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ExampleViewer {
|
||||
// TODO: replace by signal-based input when it'll be available
|
||||
@Input({required: true}) set metadata(value: ExampleMetadata) {
|
||||
this.exampleMetadata.set(value);
|
||||
}
|
||||
exampleMetadata = input.required<ExampleMetadata>({alias: 'metadata'});
|
||||
|
||||
@Input() githubUrl: string | null = null;
|
||||
@Input() stackblitzUrl: string | null = null;
|
||||
@ViewChild('codeTabs') matTabGroup?: MatTabGroup;
|
||||
readonly matTabGroup = viewChild<MatTabGroup>('codeTabs');
|
||||
|
||||
private readonly changeDetector = inject(ChangeDetectorRef);
|
||||
private readonly clipboard = inject(Clipboard);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly injector = inject(Injector);
|
||||
private readonly elementRef = inject(ElementRef<HTMLElement>);
|
||||
private readonly exampleViewerContentLoader = inject(EXAMPLE_VIEWER_CONTENT_LOADER);
|
||||
|
||||
@@ -74,8 +75,8 @@ export class ExampleViewer {
|
||||
CodeExampleViewMode = CodeExampleViewMode;
|
||||
exampleComponent?: Type<unknown>;
|
||||
|
||||
expandable = signal<boolean>(false);
|
||||
expanded = signal<boolean>(false);
|
||||
exampleMetadata = signal<ExampleMetadata | null>(null);
|
||||
snippetCode = signal<Snippet | undefined>(undefined);
|
||||
tabs = computed(() =>
|
||||
this.exampleMetadata()?.files.map((file) => ({
|
||||
@@ -89,9 +90,6 @@ export class ExampleViewer {
|
||||
? CodeExampleViewMode.SNIPPET
|
||||
: CodeExampleViewMode.MULTI_FILE,
|
||||
);
|
||||
expandable = computed(() =>
|
||||
this.exampleMetadata()?.files.some((file) => !!file.visibleLinesRange),
|
||||
);
|
||||
|
||||
async renderExample(): Promise<void> {
|
||||
// Lazy load live example component
|
||||
@@ -103,18 +101,27 @@ export class ExampleViewer {
|
||||
|
||||
this.snippetCode.set(this.exampleMetadata()?.files[0]);
|
||||
|
||||
this.changeDetector.detectChanges();
|
||||
afterNextRender(
|
||||
() => {
|
||||
// Several function below query the DOM directly, we need to wait until the DOM is rendered.
|
||||
this.setCodeLinesVisibility();
|
||||
|
||||
this.setCodeLinesVisibility();
|
||||
this.elementRef.nativeElement.setAttribute(
|
||||
'id',
|
||||
`example-${this.exampleMetadata()?.id.toString()!}`,
|
||||
);
|
||||
|
||||
this.elementRef.nativeElement.setAttribute(
|
||||
'id',
|
||||
`example-${this.exampleMetadata()?.id.toString()!}`,
|
||||
this.matTabGroup()?.realignInkBar();
|
||||
|
||||
this.listenToMatTabIndexChange();
|
||||
|
||||
const lines = this.getHiddenCodeLines();
|
||||
const lineNumbers = this.getHiddenCodeLineNumbers();
|
||||
|
||||
this.expandable.set(lines.length > 0 || lineNumbers.length > 0);
|
||||
},
|
||||
{injector: this.injector},
|
||||
);
|
||||
|
||||
this.matTabGroup?.realignInkBar();
|
||||
|
||||
this.listenToMatTabIndexChange();
|
||||
}
|
||||
|
||||
toggleExampleVisibility(): void {
|
||||
@@ -135,8 +142,9 @@ export class ExampleViewer {
|
||||
}
|
||||
|
||||
private listenToMatTabIndexChange(): void {
|
||||
this.matTabGroup?.realignInkBar();
|
||||
this.matTabGroup?.selectedIndexChange
|
||||
const matTabGroup = this.matTabGroup();
|
||||
matTabGroup?.realignInkBar();
|
||||
matTabGroup?.selectedIndexChange
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((index) => {
|
||||
this.snippetCode.set(this.exampleMetadata()?.files[index]);
|
||||
@@ -157,21 +165,9 @@ export class ExampleViewer {
|
||||
}
|
||||
|
||||
private handleExpandedStateForCodeBlock(): void {
|
||||
const lines = <HTMLDivElement[]>(
|
||||
Array.from(
|
||||
this.elementRef.nativeElement.querySelectorAll(
|
||||
`.${CODE_LINE_CLASS_NAME}.${HIDDEN_CLASS_NAME}`,
|
||||
),
|
||||
)
|
||||
);
|
||||
const lines = this.getHiddenCodeLines();
|
||||
|
||||
const lineNumbers = <HTMLSpanElement[]>(
|
||||
Array.from(
|
||||
this.elementRef.nativeElement.querySelectorAll(
|
||||
`.${CODE_LINE_NUMBER_CLASS_NAME}.${HIDDEN_CLASS_NAME}`,
|
||||
),
|
||||
)
|
||||
);
|
||||
const lineNumbers = this.getHiddenCodeLineNumbers();
|
||||
|
||||
const gapLines = <HTMLDivElement[]>(
|
||||
Array.from(
|
||||
@@ -239,4 +235,24 @@ export class ExampleViewer {
|
||||
element.parentNode?.insertBefore(separator, element);
|
||||
}
|
||||
}
|
||||
|
||||
private getHiddenCodeLines(): HTMLDivElement[] {
|
||||
return <HTMLDivElement[]>(
|
||||
Array.from(
|
||||
this.elementRef.nativeElement.querySelectorAll(
|
||||
`.${CODE_LINE_CLASS_NAME}.${HIDDEN_CLASS_NAME}`,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private getHiddenCodeLineNumbers(): HTMLSpanElement[] {
|
||||
return <HTMLSpanElement[]>(
|
||||
Array.from(
|
||||
this.elementRef.nativeElement.querySelectorAll(
|
||||
`.${CODE_LINE_NUMBER_CLASS_NAME}.${HIDDEN_CLASS_NAME}`,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
ts_library(
|
||||
name = "constants",
|
||||
srcs = [
|
||||
"index.ts",
|
||||
],
|
||||
visibility = [
|
||||
"//adev/shared-docs:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":lib",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "lib",
|
||||
srcs = glob(
|
||||
[
|
||||
"*.ts",
|
||||
],
|
||||
exclude = [
|
||||
"index.ts",
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* 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.dev/license
|
||||
*/
|
||||
|
||||
// Used for both the table of contents and the home animation
|
||||
export const RESIZE_EVENT_DELAY = 500;
|
||||
|
||||
// Used for the home animation
|
||||
export const WEBGL_LOADED_DELAY = 250;
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {Directive, ElementRef, Input, inject, signal} from '@angular/core';
|
||||
import {Highlightable} from '@angular/cdk/a11y';
|
||||
import {SearchResult} from '../../interfaces/search-results';
|
||||
import {SearchResultItem} from '../../interfaces';
|
||||
|
||||
@Directive({
|
||||
selector: '[docsSearchItem]',
|
||||
@@ -17,7 +17,7 @@ import {SearchResult} from '../../interfaces/search-results';
|
||||
},
|
||||
})
|
||||
export class SearchItem implements Highlightable {
|
||||
@Input() item?: SearchResult;
|
||||
@Input() item?: SearchResultItem;
|
||||
@Input() disabled = false;
|
||||
|
||||
private readonly elementRef = inject(ElementRef<HTMLLIElement>);
|
||||
@@ -36,14 +36,6 @@ export class SearchItem implements Highlightable {
|
||||
this._isActive.set(false);
|
||||
}
|
||||
|
||||
getLabel(): string {
|
||||
if (!this.item?.hierarchy) {
|
||||
return '';
|
||||
}
|
||||
const {hierarchy} = this.item;
|
||||
return `${hierarchy.lvl0}${hierarchy.lvl1}${hierarchy.lvl2}`;
|
||||
}
|
||||
|
||||
scrollIntoView(): void {
|
||||
this.elementRef?.nativeElement.scrollIntoView({block: 'nearest'});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
load("//adev/shared-docs/pipeline:_guides.bzl", _generate_guides = "generate_guides")
|
||||
load("//adev/shared-docs/pipeline:_navigation.bzl", _generate_nav_items = "generate_nav_items")
|
||||
load("//adev/shared-docs/pipeline:_playground.bzl", _generate_playground = "generate_playground")
|
||||
load("//adev/shared-docs/pipeline:_previews.bzl", _generate_previews = "generate_previews")
|
||||
load("//adev/shared-docs/pipeline:_stackblitz.bzl", _generate_stackblitz = "generate_stackblitz")
|
||||
load("//adev/shared-docs/pipeline:_tutorial.bzl", _generate_tutorial = "generate_tutorial")
|
||||
|
||||
generate_guides = _generate_guides
|
||||
generate_stackblitz = _generate_stackblitz
|
||||
generate_previews = _generate_previews
|
||||
generate_playground = _generate_playground
|
||||
generate_tutorial = _generate_tutorial
|
||||
generate_nav_items = _generate_nav_items
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
export * from './directives/index';
|
||||
export * from './components/index';
|
||||
export * from './constants/index';
|
||||
export * from './interfaces/index';
|
||||
export * from './providers/index';
|
||||
export * from './services/index';
|
||||
|
||||
@@ -54,3 +54,14 @@ export interface Hierarchy {
|
||||
lvl5: string | null;
|
||||
lvl6: string | null;
|
||||
}
|
||||
|
||||
/** Parsed & Structured search results */
|
||||
export interface SearchResultItem {
|
||||
type: 'doc' | 'code';
|
||||
labelHtml: string | null;
|
||||
subLabelHtml: string | null;
|
||||
url: string;
|
||||
|
||||
id: string;
|
||||
category: string | null;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,4 @@ export interface TableOfContentsItem {
|
||||
level: TableOfContentsLevel;
|
||||
/** The unique title for this document page. */
|
||||
title: string;
|
||||
/** The top offset px of the heading */
|
||||
top: number;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"emoji-regex": "~10.4.0",
|
||||
"fast-glob": "~3.3.2",
|
||||
"fflate": "^0.8.2",
|
||||
"html-entities": "~2.5.2",
|
||||
"jsdom": "~26.0.0",
|
||||
"html-entities": "~2.6.0",
|
||||
"jsdom": "~26.1.0",
|
||||
"marked": "~15.0.0",
|
||||
"mermaid": "^11.0.0",
|
||||
"shiki": "^3.0.0"
|
||||
|
||||
@@ -61,6 +61,22 @@ esbuild_esm_bundle(
|
||||
],
|
||||
)
|
||||
|
||||
esbuild_esm_bundle(
|
||||
name = "previews-bundle",
|
||||
entry_point = "//adev/shared-docs/pipeline/examples/previews:index.ts",
|
||||
external = [
|
||||
"typescript",
|
||||
],
|
||||
output = "previews.mjs",
|
||||
platform = "node",
|
||||
target = "es2022",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//adev/shared-docs/pipeline/examples/previews:index",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
|
||||
esbuild_esm_bundle(
|
||||
name = "zip-bundle",
|
||||
entry_point = "//adev/shared-docs/pipeline/examples/zip:index.ts",
|
||||
@@ -112,6 +128,7 @@ esbuild_esm_bundle(
|
||||
exports_files([
|
||||
"_guides.bzl",
|
||||
"_stackblitz.bzl",
|
||||
"_previews.bzl",
|
||||
"_playground.bzl",
|
||||
"_tutorial.bzl",
|
||||
"_navigation.bzl",
|
||||
@@ -129,6 +146,15 @@ nodejs_binary(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "previews",
|
||||
data = [
|
||||
"@npm//typescript",
|
||||
],
|
||||
entry_point = "//adev/shared-docs/pipeline:previews.mjs",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "zip",
|
||||
entry_point = "//adev/shared-docs/pipeline:zip.mjs",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
load("@build_bazel_rules_nodejs//:providers.bzl", "run_node")
|
||||
|
||||
def _generate_previews_impl(ctx):
|
||||
"""Implementation of the previews generator rule"""
|
||||
|
||||
# File declaration of the generated ts file
|
||||
ts_output = ctx.actions.declare_file("previews.ts")
|
||||
|
||||
# Set the arguments for the actions inputs and out put location.
|
||||
args = ctx.actions.args()
|
||||
|
||||
# Path to the examples for which previews are being generated.
|
||||
args.add(ctx.attr.example_srcs.label.package)
|
||||
|
||||
# Path to the preview map template.
|
||||
args.add(ctx.file._template_src)
|
||||
|
||||
# Path to the ts output file to write to.
|
||||
args.add(ts_output.path)
|
||||
|
||||
ctx.runfiles(files = ctx.files._template_src)
|
||||
|
||||
run_node(
|
||||
ctx = ctx,
|
||||
inputs = depset(ctx.files.example_srcs + ctx.files._template_src),
|
||||
executable = "_generate_previews",
|
||||
outputs = [ts_output],
|
||||
arguments = [args],
|
||||
)
|
||||
|
||||
# The return value describes what the rule is producing. In this case we need to specify
|
||||
# the "DefaultInfo" with the output ts file.
|
||||
return [DefaultInfo(files = depset([ts_output]))]
|
||||
|
||||
generate_previews = rule(
|
||||
# Point to the starlark function that will execute for this rule.
|
||||
implementation = _generate_previews_impl,
|
||||
doc = """Rule that generates a map of example previews to their component""",
|
||||
|
||||
# The attributes that can be set to this rule.
|
||||
attrs = {
|
||||
"example_srcs": attr.label(
|
||||
doc = """Files used for the previews map generation.""",
|
||||
),
|
||||
"_template_src": attr.label(
|
||||
doc = """The previews map template file to base the generated file on.""",
|
||||
default = Label("//adev/shared-docs/pipeline/examples/previews:template"),
|
||||
allow_single_file = True,
|
||||
),
|
||||
"_generate_previews": attr.label(
|
||||
default = Label("//adev/shared-docs/pipeline:previews"),
|
||||
executable = True,
|
||||
cfg = "exec",
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -133,7 +133,8 @@ export interface DirectiveEntry extends ClassEntry {
|
||||
export interface PipeEntry extends ClassEntry {
|
||||
pipeName: string;
|
||||
isStandalone: boolean;
|
||||
// TODO: add `isPure`.
|
||||
isPure: boolean;
|
||||
usage: string;
|
||||
}
|
||||
|
||||
export interface FunctionSignatureMetadata extends DocEntry {
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
JsDocTagEntry,
|
||||
MemberEntry,
|
||||
ParameterEntry,
|
||||
PipeEntry,
|
||||
TypeAliasEntry,
|
||||
} from '../entities';
|
||||
|
||||
@@ -60,6 +61,12 @@ export type ClassEntryRenderable = ClassEntry &
|
||||
members: MemberEntryRenderable[];
|
||||
};
|
||||
|
||||
export type PipeEntryRenderable = PipeEntry &
|
||||
DocEntryRenderable &
|
||||
HasRenderableToc & {
|
||||
members: MemberEntryRenderable[];
|
||||
};
|
||||
|
||||
export type DecoratorEntryRenderable = DecoratorEntry &
|
||||
DocEntryRenderable &
|
||||
HasRenderableToc & {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import {Renderer, Tokens} from 'marked';
|
||||
import {codeToHtml} from '../shiki/shiki';
|
||||
import {SECTION_HEADING, SECTION_SUB_HEADING} from '../styling/css-classes';
|
||||
|
||||
/**
|
||||
* Custom renderer for marked that will be used to transform markdown files to HTML
|
||||
@@ -55,21 +56,39 @@ export const renderer: Partial<Renderer> = {
|
||||
<div class="docs-table docs-scroll-track-transparent">
|
||||
<table>
|
||||
<thead>
|
||||
${this.tablerow({
|
||||
text: header.map((cell) => this.tablecell(cell)).join(''),
|
||||
})}
|
||||
${this.tablerow({text: header.map((cell) => this.tablecell(cell)).join('')})}
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows
|
||||
.map((row) =>
|
||||
this.tablerow({
|
||||
text: row.map((cell) => this.tablecell(cell)).join(''),
|
||||
}),
|
||||
)
|
||||
.map((row) => this.tablerow({text: row.map((cell) => this.tablecell(cell)).join('')}))
|
||||
.join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
heading(this: Renderer, {text, depth, tokens}: Tokens.Heading) {
|
||||
const id = text
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '-')
|
||||
.replace(/[^a-z0-9-]/g, '');
|
||||
|
||||
// Since we have a code transformer `addApiLinksToHtml` which adds anchors
|
||||
// to code blocks of known symbols, we add an additional `data-skip-anchor`
|
||||
// attribute that prevents the transformation. This is needed since nested
|
||||
// anchor tags are illegal and break the HTML.
|
||||
const textRenderer = new Renderer();
|
||||
textRenderer.codespan = ({text}) => `<code data-skip-anchor>${text}</code>`;
|
||||
const parsedText = this.parser.parseInline(tokens, textRenderer);
|
||||
|
||||
// The template matches templates/section-heading.tsx
|
||||
return `
|
||||
<h${depth} id="${id}" class="${SECTION_HEADING} ${SECTION_SUB_HEADING}">
|
||||
<a href="#${id}" aria-label="Link to ${text} section" tabIndex="-1">
|
||||
${parsedText}
|
||||
</a>
|
||||
</h${depth}>
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,3 +27,4 @@ export const HEADER_ENTRY_LABEL = 'docs-api-item-label';
|
||||
|
||||
export const SECTION_CONTAINER = 'docs-reference-section';
|
||||
export const SECTION_HEADING = 'docs-reference-section-heading';
|
||||
export const SECTION_SUB_HEADING = 'docs-reference-section-heading--sub';
|
||||
|
||||
@@ -38,8 +38,6 @@ export function getCurrentSymbol(): string | undefined {
|
||||
return currentSymbol;
|
||||
}
|
||||
|
||||
export function logUnknownSymbol(link: string, symbol: string): void {
|
||||
console.warn(
|
||||
`WARNING: {@link ${link}} is invalid, ${symbol} or ${currentSymbol}.${symbol} is unknown in this context`,
|
||||
);
|
||||
export function unknownSymbolMessage(link: string, symbol: string): string {
|
||||
return `WARNING: {@link ${link}} is invalid, ${symbol} or ${currentSymbol}.${symbol} is unknown in this context`;
|
||||
}
|
||||
|
||||
@@ -7,19 +7,43 @@
|
||||
*/
|
||||
|
||||
import {Fragment, h} from 'preact';
|
||||
import {ClassEntryRenderable, DecoratorEntryRenderable} from '../entities/renderables';
|
||||
import {
|
||||
ClassEntryRenderable,
|
||||
DecoratorEntryRenderable,
|
||||
PipeEntryRenderable,
|
||||
} from '../entities/renderables';
|
||||
import {ClassMemberList} from './class-member-list';
|
||||
import {HeaderApi} from './header-api';
|
||||
import {API_REFERENCE_CONTAINER, REFERENCE_MEMBERS} from '../styling/css-classes';
|
||||
import {
|
||||
API_REFERENCE_CONTAINER,
|
||||
REFERENCE_MEMBERS,
|
||||
SECTION_CONTAINER,
|
||||
} from '../styling/css-classes';
|
||||
import {SectionDescription} from './section-description';
|
||||
import {SectionUsageNotes} from './section-usage-notes';
|
||||
import {SectionApi} from './section-api';
|
||||
import {SectionHeading} from './section-heading';
|
||||
import {PipeEntry} from '../entities';
|
||||
import {codeToHtml} from '../shiki/shiki';
|
||||
import {RawHtml} from './raw-html';
|
||||
|
||||
/** Component to render a class API reference document. */
|
||||
export function ClassReference(entry: ClassEntryRenderable | DecoratorEntryRenderable) {
|
||||
export function ClassReference(
|
||||
entry: ClassEntryRenderable | DecoratorEntryRenderable | PipeEntryRenderable,
|
||||
) {
|
||||
return (
|
||||
<div className={API_REFERENCE_CONTAINER}>
|
||||
<HeaderApi entry={entry} />
|
||||
{entry.entryType === 'pipe' ? (
|
||||
<>
|
||||
<div className={SECTION_CONTAINER + ' docs-reference-api-section'}>
|
||||
<SectionHeading name="Pipe usage" />
|
||||
<RawHtml value={codeToHtml((entry as PipeEntry).usage, 'angular-html')} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<SectionApi entry={entry} />
|
||||
{entry.members.length > 0 ? (
|
||||
<div class={REFERENCE_MEMBERS}>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import {h} from 'preact';
|
||||
import {EntryType, isDocEntryWithSourceInfo} from '../entities';
|
||||
import {DocEntryRenderable} from '../entities/renderables';
|
||||
import {EntryType, isDocEntryWithSourceInfo, PipeEntry} from '../entities';
|
||||
import {DocEntryRenderable, PipeEntryRenderable} from '../entities/renderables';
|
||||
import {
|
||||
HEADER_CLASS_NAME,
|
||||
HEADER_ENTRY_CATEGORY,
|
||||
@@ -18,16 +18,14 @@ import {
|
||||
import {DocsPillRow} from './docs-pill-row';
|
||||
|
||||
/** Component to render a header of the API page. */
|
||||
export function HeaderApi(props: {entry: DocEntryRenderable; showFullDescription?: boolean}) {
|
||||
export function HeaderApi(props: {entry: DocEntryRenderable | PipeEntryRenderable; showFullDescription?: boolean}) {
|
||||
const entry = props.entry;
|
||||
|
||||
// TODO: This link point to the main branch.
|
||||
// When ADEV is not deployed on the main branch branch anymore,
|
||||
// We should update it to point to the tag of the released version which ADEV runs on.
|
||||
|
||||
const sourceUrl = isDocEntryWithSourceInfo(entry)
|
||||
? `https://github.com/angular/angular/blob/main${entry.source.filePath}#L${entry.source.startLine}-L${entry.source.endLine}`
|
||||
: null;
|
||||
const sourceUrl = sourceUrlForEntry(entry);
|
||||
|
||||
return (
|
||||
<header className={HEADER_CLASS_NAME}>
|
||||
@@ -40,9 +38,7 @@ export function HeaderApi(props: {entry: DocEntryRenderable; showFullDescription
|
||||
{getEntryTypeDisplayName(entry.entryType)}
|
||||
</div>
|
||||
{entry.isDeprecated && (
|
||||
<div className={`${HEADER_ENTRY_LABEL} type-deprecated full`}>
|
||||
Deprecated
|
||||
</div>
|
||||
<div className={`${HEADER_ENTRY_LABEL} type-deprecated full`}>Deprecated</div>
|
||||
)}
|
||||
{entry.isDeveloperPreview && (
|
||||
<div className={`${HEADER_ENTRY_LABEL} type-developer_preview full`}>
|
||||
@@ -54,6 +50,12 @@ export function HeaderApi(props: {entry: DocEntryRenderable; showFullDescription
|
||||
<a href="/reference/releases#experimental">Experimental</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{entry.entryType === EntryType.Pipe && !(entry as PipeEntry).isPure && (
|
||||
<div className={`${HEADER_ENTRY_LABEL} type-impure-pipe full`}>
|
||||
Impure
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{sourceUrl && (
|
||||
<a
|
||||
@@ -95,3 +97,16 @@ function getEntryTypeDisplayName(entryType: EntryType | string): string {
|
||||
}
|
||||
return entryType;
|
||||
}
|
||||
|
||||
function sourceUrlForEntry(entry: DocEntryRenderable): string | null {
|
||||
if (!isDocEntryWithSourceInfo(entry)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (entry.source.filePath.includes('node_modules')) {
|
||||
// We don't know the source path in external repos link the CLI
|
||||
return null;
|
||||
} else {
|
||||
return `https://github.com/angular/angular/blob/main${entry.source.filePath}#L${entry.source.startLine}-L${entry.source.endLine}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,6 +754,525 @@
|
||||
"startLine": 112,
|
||||
"endLine": 115
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "HttpRequest",
|
||||
"isAbstract": false,
|
||||
"entryType": "undecorated_class",
|
||||
"members": [
|
||||
{
|
||||
"name": "constructor",
|
||||
"signatures": [
|
||||
{
|
||||
"name": "constructor",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "\"GET\" | \"HEAD\"",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"returnType": "foobar",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"generics": [],
|
||||
"isNewType": false
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "\"DELETE\" | \"JSONP\" | \"OPTIONS\"",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"returnType": "foobar",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"generics": [],
|
||||
"isNewType": false
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "\"POST\"",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"type": "T | null",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"returnType": "foobar",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"generics": [],
|
||||
"isNewType": false
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "\"PUT\" | \"PATCH\"",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"type": "T | null",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"returnType": "foobar",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"generics": [],
|
||||
"isNewType": false
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"description": "",
|
||||
"type": "T | null",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "init",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"returnType": "foobar",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"generics": [],
|
||||
"isNewType": false
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "third",
|
||||
"description": "",
|
||||
"type": "T | { headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | null | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "fourth",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"returnType": "foobar",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"generics": [],
|
||||
"isNewType": false
|
||||
}
|
||||
],
|
||||
"implementation": {
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "",
|
||||
"type": "string",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "third",
|
||||
"description": "",
|
||||
"type": "T | { headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | null | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
},
|
||||
{
|
||||
"name": "fourth",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; } | undefined",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"isNewType": false,
|
||||
"returnType": "HttpRequest<T>",
|
||||
"generics": [],
|
||||
"name": "constructor",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": ""
|
||||
},
|
||||
"entryType": "function",
|
||||
"description": "",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"memberType": "method",
|
||||
"memberTags": []
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"type": "T | null",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "The request body, or `null` if one isn't set.\n\nBodies are not enforced to be immutable, as they can include a reference to any\nuser-defined data type. However, interceptors should take care to preserve\nidempotence by treating them as such.",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "headers",
|
||||
"type": "HttpHeaders",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "Outgoing headers for this request.",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "context",
|
||||
"type": "HttpContext",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "Shared and mutable context that can be used by interceptors",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "reportProgress",
|
||||
"type": "boolean",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "Whether this request should be made in a way that exposes progress events.\n\nProgress events are expensive (change detection runs on each event) and so\nthey should only be requested if the consumer intends to monitor them.\n\nNote: The `FetchBackend` doesn't support progress report on uploads.",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "withCredentials",
|
||||
"type": "boolean",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "Whether this request should be sent with outgoing credentials (cookies).",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "responseType",
|
||||
"type": "\"arraybuffer\" | \"blob\" | \"text\" | \"json\"",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "The expected response type of the server.\n\nThis is used to parse the response appropriately before returning it to\nthe requestee.",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "method",
|
||||
"type": "string",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "The outgoing HTTP request method.",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"type": "HttpParams",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "Outgoing URL parameters.\n\nTo pass a string representation of HTTP parameters in the URL-query-string format,\nthe `HttpParamsOptions`' `fromString` may be used. For example:\n\n```ts\nnew HttpParams({fromString: 'angular=awesome'})\n```",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "urlWithParams",
|
||||
"type": "string",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly"],
|
||||
"description": "The outgoing URL with all URL parameters set.",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "transferCache",
|
||||
"type": "boolean | { includeHeaders?: string[] | undefined; } | undefined",
|
||||
"memberType": "property",
|
||||
"memberTags": ["readonly", "optional"],
|
||||
"description": "The HttpTransferCache option for the request",
|
||||
"jsdocTags": []
|
||||
},
|
||||
{
|
||||
"name": "serializeBody",
|
||||
"signatures": [
|
||||
{
|
||||
"name": "serializeBody",
|
||||
"entryType": "function",
|
||||
"description": "Transform the free-form body into a serialized format suitable for\ntransmission to the server.",
|
||||
"generics": [],
|
||||
"isNewType": false,
|
||||
"jsdocTags": [],
|
||||
"params": [],
|
||||
"rawComment": "/**\n * Transform the free-form body into a serialized format suitable for\n * transmission to the server.\n */",
|
||||
"returnType": "string | ArrayBuffer | Blob | FormData | URLSearchParams | null"
|
||||
}
|
||||
],
|
||||
"implementation": {
|
||||
"params": [],
|
||||
"isNewType": false,
|
||||
"returnType": "string | ArrayBuffer | Blob | FormData | URLSearchParams | null",
|
||||
"generics": [],
|
||||
"name": "serializeBody",
|
||||
"description": "Transform the free-form body into a serialized format suitable for\ntransmission to the server.",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "/**\n * Transform the free-form body into a serialized format suitable for\n * transmission to the server.\n */"
|
||||
},
|
||||
"entryType": "function",
|
||||
"description": "Transform the free-form body into a serialized format suitable for\ntransmission to the server.",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "/**\n * Transform the free-form body into a serialized format suitable for\n * transmission to the server.\n */",
|
||||
"memberType": "method",
|
||||
"memberTags": []
|
||||
},
|
||||
{
|
||||
"name": "detectContentTypeHeader",
|
||||
"signatures": [
|
||||
{
|
||||
"name": "detectContentTypeHeader",
|
||||
"entryType": "function",
|
||||
"description": "Examine the body and attempt to infer an appropriate MIME type\nfor it.\n\nIf no such type can be inferred, this method will return `null`.",
|
||||
"generics": [],
|
||||
"isNewType": false,
|
||||
"jsdocTags": [],
|
||||
"params": [],
|
||||
"rawComment": "/**\n * Examine the body and attempt to infer an appropriate MIME type\n * for it.\n *\n * If no such type can be inferred, this method will return `null`.\n */",
|
||||
"returnType": "string | null"
|
||||
}
|
||||
],
|
||||
"implementation": {
|
||||
"params": [],
|
||||
"isNewType": false,
|
||||
"returnType": "string | null",
|
||||
"generics": [],
|
||||
"name": "detectContentTypeHeader",
|
||||
"description": "Examine the body and attempt to infer an appropriate MIME type\nfor it.\n\nIf no such type can be inferred, this method will return `null`.",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "/**\n * Examine the body and attempt to infer an appropriate MIME type\n * for it.\n *\n * If no such type can be inferred, this method will return `null`.\n */"
|
||||
},
|
||||
"entryType": "function",
|
||||
"description": "Examine the body and attempt to infer an appropriate MIME type\nfor it.\n\nIf no such type can be inferred, this method will return `null`.",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "/**\n * Examine the body and attempt to infer an appropriate MIME type\n * for it.\n *\n * If no such type can be inferred, this method will return `null`.\n */",
|
||||
"memberType": "method",
|
||||
"memberTags": []
|
||||
},
|
||||
{
|
||||
"name": "clone",
|
||||
"signatures": [
|
||||
{
|
||||
"name": "clone",
|
||||
"entryType": "function",
|
||||
"description": "",
|
||||
"generics": [],
|
||||
"isNewType": false,
|
||||
"jsdocTags": [],
|
||||
"params": [],
|
||||
"rawComment": "",
|
||||
"returnType": "HttpRequest<T>"
|
||||
},
|
||||
{
|
||||
"name": "clone",
|
||||
"entryType": "function",
|
||||
"description": "",
|
||||
"generics": [],
|
||||
"isNewType": false,
|
||||
"jsdocTags": [],
|
||||
"params": [
|
||||
{
|
||||
"name": "update",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; body?: T | null | undefined; method?: string | undefined; url?: string | undefined; setHeaders?: { [name: string]: string | string[]; } | undefined; setParams?: { [param: string]: string; } | undefined; }",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"rawComment": "",
|
||||
"returnType": "HttpRequest<T>"
|
||||
},
|
||||
{
|
||||
"name": "clone",
|
||||
"entryType": "function",
|
||||
"description": "",
|
||||
"generics": [{"name": "V"}],
|
||||
"isNewType": false,
|
||||
"jsdocTags": [],
|
||||
"params": [
|
||||
{
|
||||
"name": "update",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; body?: V | null | undefined; method?: string | undefined; url?: string | undefined; setHeaders?: { [name: string]: string | string[]; } | undefined; setParams?: { [param: string]: string; } | undefined; }",
|
||||
"isOptional": false,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"rawComment": "",
|
||||
"returnType": "HttpRequest<V>"
|
||||
}
|
||||
],
|
||||
"implementation": {
|
||||
"params": [
|
||||
{
|
||||
"name": "update",
|
||||
"description": "",
|
||||
"type": "{ headers?: HttpHeaders | undefined; context?: HttpContext | undefined; reportProgress?: boolean | undefined; params?: HttpParams | undefined; responseType?: \"arraybuffer\" | \"blob\" | \"text\" | \"json\" | undefined; withCredentials?: boolean | undefined; transferCache?: boolean | { includeHeaders?: string[] | undefined; } | undefined; body?: any; method?: string | undefined; url?: string | undefined; setHeaders?: { [name: string]: string | string[]; } | undefined; setParams?: { [param: string]: string; } | undefined; }",
|
||||
"isOptional": true,
|
||||
"isRestParam": false
|
||||
}
|
||||
],
|
||||
"isNewType": false,
|
||||
"returnType": "HttpRequest<any>",
|
||||
"generics": [],
|
||||
"name": "clone",
|
||||
"description": "",
|
||||
"entryType": "function",
|
||||
"jsdocTags": [],
|
||||
"rawComment": ""
|
||||
},
|
||||
"entryType": "function",
|
||||
"description": "",
|
||||
"jsdocTags": [],
|
||||
"rawComment": "",
|
||||
"memberType": "method",
|
||||
"memberTags": []
|
||||
}
|
||||
],
|
||||
"generics": [{"name": "T"}],
|
||||
"description": "An outgoing HTTP request with an optional typed body.\n\n`HttpRequest` represents an outgoing request, including URL, method,\nheaders, body, and other request configuration options. Instances should be\nassumed to be immutable. To modify a `HttpRequest`, the `clone`\nmethod should be used.",
|
||||
"jsdocTags": [{"name": "publicApi", "comment": ""}],
|
||||
"rawComment": "/**\n * An outgoing HTTP request with an optional typed body.\n *\n * `HttpRequest` represents an outgoing request, including URL, method,\n * headers, body, and other request configuration options. Instances should be\n * assumed to be immutable. To modify a `HttpRequest`, the `clone`\n * method should be used.\n *\n * @publicApi\n */",
|
||||
"implements": [],
|
||||
"source": {
|
||||
"filePath": "/packages/common/http/src/request.ts",
|
||||
"startLine": 131,
|
||||
"endLine": 578
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ describe('markdown to html', () => {
|
||||
const symbols = new Map<string, string>([
|
||||
['AfterRenderPhase', 'core'],
|
||||
['afterRender', 'core'],
|
||||
['EmbeddedViewRef', 'core'],
|
||||
['ChangeDetectionStrategy', 'core'],
|
||||
['ChangeDetectorRef', 'core'],
|
||||
['withNoHttpTransferCache', 'platform-browser'],
|
||||
['withHttpTransferCacheOptions', 'platform-browser'],
|
||||
['withI18nSupport', 'platform-browser'],
|
||||
['withEventReplay', 'platform-browser'],
|
||||
]);
|
||||
setSymbols(symbols);
|
||||
for (const entry of entryJson.entries) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {getRenderable} from '../processing';
|
||||
import {DocEntryRenderable} from '../entities/renderables';
|
||||
import {initHighlighter} from '../shiki/shiki';
|
||||
import {configureMarkedGlobally} from '../marked/configuration';
|
||||
import {setSymbols} from '../symbol-context';
|
||||
|
||||
// Note: The tests will probably break if the schema of the api extraction changes.
|
||||
// All entries in the fake-entries are extracted from Angular's api.
|
||||
@@ -28,6 +29,19 @@ describe('renderable', () => {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
const entryJson = JSON.parse(entryContent) as any;
|
||||
const symbols = new Map<string, string>([
|
||||
['AfterRenderPhase', 'core'],
|
||||
['afterRender', 'core'],
|
||||
['EmbeddedViewRef', 'core'],
|
||||
['ChangeDetectionStrategy', 'core'],
|
||||
['ChangeDetectorRef', 'core'],
|
||||
['withNoHttpTransferCache', 'platform-browser'],
|
||||
['withHttpTransferCacheOptions', 'platform-browser'],
|
||||
['withI18nSupport', 'platform-browser'],
|
||||
['withEventReplay', 'platform-browser'],
|
||||
]);
|
||||
setSymbols(symbols);
|
||||
|
||||
for (const entry of entryJson.entries) {
|
||||
const renderableJson = getRenderable(entry, '@angular/fakeentry') as DocEntryRenderable;
|
||||
entries.set(entry['name'], renderableJson);
|
||||
|
||||
@@ -66,6 +66,14 @@ describe('jsdoc transforms', () => {
|
||||
name: 'see',
|
||||
comment: '{@link https://angular.dev}',
|
||||
},
|
||||
{
|
||||
name: 'see',
|
||||
comment: '{@link /cli/build ng build}',
|
||||
},
|
||||
{
|
||||
name: 'see',
|
||||
comment: '{@link /ecosystem/rxjs-interop/output-interop Output Interop}',
|
||||
},
|
||||
],
|
||||
moduleName: 'test',
|
||||
});
|
||||
@@ -117,5 +125,30 @@ describe('jsdoc transforms', () => {
|
||||
label: 'angular.dev',
|
||||
url: 'https://angular.dev',
|
||||
});
|
||||
|
||||
expect(entry.additionalLinks[10]).toEqual({
|
||||
label: 'ng build',
|
||||
url: '/cli/build',
|
||||
});
|
||||
|
||||
expect(entry.additionalLinks[11]).toEqual({
|
||||
label: 'Output Interop',
|
||||
url: '/ecosystem/rxjs-interop/output-interop',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw on invalid relatie @link', () => {
|
||||
const entryFn = () =>
|
||||
addHtmlAdditionalLinks({
|
||||
jsdocTags: [
|
||||
{
|
||||
name: 'see',
|
||||
comment: '{@link cli/build ng build}',
|
||||
},
|
||||
],
|
||||
moduleName: 'test',
|
||||
});
|
||||
|
||||
expect(entryFn).toThrowError(/Forbidden relative link: cli\/build ng build/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -476,10 +476,11 @@ function appendPrefixAndSuffix(entry: DocEntry, codeTocData: CodeTableOfContents
|
||||
*/
|
||||
export function addApiLinksToHtml(htmlString: string): string {
|
||||
const result = htmlString.replace(
|
||||
// This regex looks for span/code blocks not wrapped by an anchor block.
|
||||
// This regex looks for span/code blocks not wrapped by an anchor block
|
||||
// or the tag doesn't contain `data-skip-anchor` attribute.
|
||||
// Their content are then replaced with a link if the symbol is known
|
||||
// The captured content ==> vvvvvvvv
|
||||
/(?<!<a[^>]*>)(<(?:(?:span)|(?:code))[^>]*>\s*)([^<]*?)(\s*<\/(?:span|code)>)/g,
|
||||
// The captured content ==> vvvvvvvv
|
||||
/(?<!<a[^>]*>)(<(?:(?:span)|(?:code))(?!\sdata-skip-anchor)[^>]*>\s*)([^<]*?)(\s*<\/(?:span|code)>)/g,
|
||||
(type: string, span1: string, potentialSymbolName: string, span2: string) => {
|
||||
let [symbol, subSymbol] = potentialSymbolName.split(/(?:#|\.)/) as [string, string?];
|
||||
|
||||
|
||||
@@ -31,10 +31,9 @@ import {
|
||||
|
||||
import {getLinkToModule} from './url-transforms';
|
||||
import {addApiLinksToHtml} from './code-transforms';
|
||||
import {getCurrentSymbol, getModuleName, logUnknownSymbol} from '../symbol-context';
|
||||
import {getCurrentSymbol, getModuleName, unknownSymbolMessage} from '../symbol-context';
|
||||
|
||||
export const JS_DOC_REMARKS_TAG = 'remarks';
|
||||
export const JS_DOC_USAGE_NOTES_TAG = 'usageNotes';
|
||||
const JS_DOC_USAGE_NOTE_TAGS: Set<string> = new Set(['remarks', 'usageNotes', 'example']);
|
||||
export const JS_DOC_SEE_TAG = 'see';
|
||||
export const JS_DOC_DESCRIPTION_TAG = 'description';
|
||||
|
||||
@@ -58,16 +57,10 @@ export function addHtmlDescription<T extends HasDescription & HasModuleName>(
|
||||
|
||||
const description = !!entry.description ? entry.description : jsDocDescription;
|
||||
const shortTextMatch = description.match(firstParagraphRule);
|
||||
const htmlDescription = getHtmlForJsDocText(description, entry).trim();
|
||||
const shortHtmlDescription = getHtmlForJsDocText(
|
||||
shortTextMatch ? shortTextMatch[0] : '',
|
||||
entry,
|
||||
).trim();
|
||||
return {
|
||||
...entry,
|
||||
htmlDescription,
|
||||
shortHtmlDescription,
|
||||
};
|
||||
const htmlDescription = getHtmlForJsDocText(description).trim();
|
||||
const shortHtmlDescription = getHtmlForJsDocText(shortTextMatch ? shortTextMatch[0] : '').trim();
|
||||
|
||||
return {...entry, htmlDescription, shortHtmlDescription};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +74,7 @@ export function addHtmlJsDocTagComments<T extends HasJsDocTags & HasModuleName>(
|
||||
...entry,
|
||||
jsdocTags: entry.jsdocTags.map((tag) => ({
|
||||
...tag,
|
||||
htmlComment: getHtmlForJsDocText(tag.comment, entry),
|
||||
htmlComment: getHtmlForJsDocText(tag.comment),
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -97,23 +90,20 @@ export function addHtmlAdditionalLinks<T extends HasJsDocTags & HasModuleName>(
|
||||
}
|
||||
|
||||
export function addHtmlUsageNotes<T extends HasJsDocTags>(entry: T): T & HasHtmlUsageNotes {
|
||||
const usageNotesTag = entry.jsdocTags.find(
|
||||
({name}) => name === JS_DOC_USAGE_NOTES_TAG || name === JS_DOC_REMARKS_TAG,
|
||||
);
|
||||
const htmlUsageNotes = usageNotesTag
|
||||
? (marked.parse(wrapExampleHtmlElementsWithCode(usageNotesTag.comment)) as string)
|
||||
: '';
|
||||
|
||||
const transformedHtml = addApiLinksToHtml(htmlUsageNotes);
|
||||
const usageNotesTags = entry.jsdocTags.filter(({name}) => JS_DOC_USAGE_NOTE_TAGS.has(name)) ?? [];
|
||||
let htmlUsageNotes = '';
|
||||
for (const {comment} of usageNotesTags) {
|
||||
htmlUsageNotes += getHtmlForJsDocText(comment);
|
||||
}
|
||||
|
||||
return {
|
||||
...entry,
|
||||
htmlUsageNotes: transformedHtml,
|
||||
htmlUsageNotes,
|
||||
};
|
||||
}
|
||||
|
||||
/** Given a markdown JsDoc text, gets the rendered HTML. */
|
||||
function getHtmlForJsDocText<T extends HasModuleName>(text: string, entry: T): string {
|
||||
function getHtmlForJsDocText(text: string): string {
|
||||
const parsed = marked.parse(convertLinks(wrapExampleHtmlElementsWithCode(text))) as string;
|
||||
return addApiLinksToHtml(parsed);
|
||||
}
|
||||
@@ -126,7 +116,7 @@ export function setEntryFlags<T extends HasJsDocTags & HasModuleName>(
|
||||
...entry,
|
||||
isDeprecated: isDeprecatedEntry(entry),
|
||||
deprecationMessage: deprecationMessage
|
||||
? getHtmlForJsDocText(deprecationMessage, entry)
|
||||
? getHtmlForJsDocText(deprecationMessage)
|
||||
: deprecationMessage,
|
||||
isDeveloperPreview: isDeveloperPreview(entry),
|
||||
isExperimental: isExperimental(entry),
|
||||
@@ -195,10 +185,16 @@ function parseAtLink(link: string): {label: string; url: string} {
|
||||
let [rawSymbol, description] = link.split(/\s(.+)/);
|
||||
if (rawSymbol.startsWith('#')) {
|
||||
rawSymbol = rawSymbol.substring(1);
|
||||
} else if (rawSymbol.startsWith('http://') || rawSymbol.startsWith('https://')) {
|
||||
} else if (rawSymbol.includes('/')) {
|
||||
if (!rawSymbol.startsWith('/') && !rawSymbol.startsWith('http')) {
|
||||
throw Error(
|
||||
`Forbidden relative link: ${link}. Links should be absolute and start with a slash`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
url: rawSymbol,
|
||||
label: rawSymbol.split('/').pop()!,
|
||||
label: description ?? rawSymbol.split('/').pop()!,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -214,11 +210,9 @@ function parseAtLink(link: string): {label: string; url: string} {
|
||||
moduleName = getModuleName(`${currentSymbol}.${symbol}`);
|
||||
|
||||
if (!moduleName || !currentSymbol) {
|
||||
// TODO: remove the links that generate this error
|
||||
// TODO: throw an error when there are no more warning generated
|
||||
logUnknownSymbol(link, symbol);
|
||||
return {label, url: '#'};
|
||||
throw unknownSymbolMessage(link, symbol);
|
||||
}
|
||||
|
||||
subSymbol = symbol;
|
||||
symbol = currentSymbol;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ts_library(
|
||||
name = "index",
|
||||
srcs = [
|
||||
"index.ts",
|
||||
],
|
||||
visibility = [
|
||||
"//adev/shared-docs:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"@npm//@types/node",
|
||||
"@npm//tinyglobby",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "template",
|
||||
srcs = ["previews.template"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
exports_files([
|
||||
"index.ts",
|
||||
"previews.template",
|
||||
])
|
||||
@@ -0,0 +1,139 @@
|
||||
/*!
|
||||
* @license
|
||||
* 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.dev/license
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
import {join, relative} from 'path';
|
||||
import {glob} from 'tinyglobby';
|
||||
import ts from 'typescript';
|
||||
|
||||
const [examplesDir, templateFilePath, outputFilePath] = process.argv.slice(2);
|
||||
|
||||
const EXAMPLES_PATH = `../../content/examples`;
|
||||
|
||||
interface File {
|
||||
path: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface AnalyzedFiles {
|
||||
path: string;
|
||||
componentNames: string[];
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
/**
|
||||
* Creates a map of example path to dynamic component import for loading embedded examples.
|
||||
*
|
||||
* For example, given the following inputs:
|
||||
* examplesDir: 'adev/src/content/examples',
|
||||
* templateFilePath: 'adev/shared-docs/pipeline/examples/previews/previews.template',
|
||||
* outputFilePath: 'bazel-out/k8-fastbuild/bin/adev/src/assets/previews/previews.ts'
|
||||
*
|
||||
* The script will generate a mapping of all example components under adev/src/content/examples and
|
||||
* fill them into the given template file, writing the result to the output file.
|
||||
*
|
||||
* It will replace the placeholder text `${previewsComponents}` in the template with mappings like:
|
||||
* ['adev/src/content/examples/accessibility/src/app/app.component.ts']:
|
||||
* () => import('../../content/examples/accessibility/src/app/app.component').then(c => c.AppComponent),
|
||||
* ['adev/src/content/examples/accessibility/src/app/progress-bar.component.ts']:
|
||||
* () => import('../../content/examples/accessibility/src/app/progress-bar.component').then(c => c.ExampleProgressbarComponent),
|
||||
* ...
|
||||
*/
|
||||
async function main() {
|
||||
const files = await glob(join(examplesDir, '**/*.ts'), {
|
||||
ignore: ['**/*.e2e-spec.ts', '**/*.spec.ts', '**/*.po.ts'],
|
||||
}).then((paths) =>
|
||||
Promise.all(
|
||||
paths.map((path) =>
|
||||
readFile(path, {encoding: 'utf-8'}).then((fileContent) => {
|
||||
return {
|
||||
path: relative(examplesDir, path),
|
||||
content: fileContent,
|
||||
};
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const filesWithComponent = files
|
||||
.map((file) => ({
|
||||
componentNames: analyzeFile(file),
|
||||
path: file.path,
|
||||
}))
|
||||
.filter((result) => result.componentNames.length > 0);
|
||||
|
||||
const previewsComponentMap = generatePreviewsComponentMap(filesWithComponent);
|
||||
|
||||
await writeFile(outputFilePath, previewsComponentMap);
|
||||
}
|
||||
|
||||
/** Returns list of the `Standalone` @Component class names for given file */
|
||||
function analyzeFile(file: File): string[] {
|
||||
const componentClassNames: string[] = [];
|
||||
const sourceFile = ts.createSourceFile(file.path, file.content, ts.ScriptTarget.Latest, false);
|
||||
|
||||
const visitNode = (node: ts.Node): void => {
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
const decorators = ts.getDecorators(node);
|
||||
const componentName = node.name ? node.name.text : null;
|
||||
|
||||
if (decorators && decorators.length) {
|
||||
for (const decorator of decorators) {
|
||||
const call = decorator.expression;
|
||||
|
||||
if (
|
||||
ts.isCallExpression(call) &&
|
||||
ts.isIdentifier(call.expression) &&
|
||||
call.expression.text === 'Component' &&
|
||||
call.arguments.length > 0 &&
|
||||
ts.isObjectLiteralExpression(call.arguments[0])
|
||||
) {
|
||||
const standaloneProperty = call.arguments[0].properties.find(
|
||||
(property) =>
|
||||
property.name &&
|
||||
ts.isIdentifier(property.name) &&
|
||||
property.name.text === 'standalone',
|
||||
);
|
||||
|
||||
const isStandalone =
|
||||
!standaloneProperty ||
|
||||
(ts.isPropertyAssignment(standaloneProperty) &&
|
||||
standaloneProperty.initializer.kind === ts.SyntaxKind.TrueKeyword);
|
||||
|
||||
if (isStandalone && componentName) {
|
||||
componentClassNames.push(componentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(node, visitNode);
|
||||
};
|
||||
|
||||
visitNode(sourceFile);
|
||||
|
||||
return componentClassNames;
|
||||
}
|
||||
|
||||
function generatePreviewsComponentMap(data: AnalyzedFiles[]): string {
|
||||
let result = '';
|
||||
for (const fileData of data) {
|
||||
for (const componentName of fileData.componentNames) {
|
||||
const key = `adev/src/content/examples/${fileData.path}${
|
||||
fileData.componentNames.length > 1 ? '_' + componentName : ''
|
||||
}`.replace(/\\/g, '/');
|
||||
result += `['${key}']: () => import('${EXAMPLES_PATH}/${fileData.path
|
||||
.replace(/\\/g, '/')
|
||||
.replace('.ts', '')}').then(c => c.${componentName}),\n`;
|
||||
}
|
||||
}
|
||||
return fs.readFileSync(templateFilePath, 'utf8').replace(/\${previewsComponents}/g, result);
|
||||
}
|
||||
+3
-7
@@ -6,16 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
/**
|
||||
******************************************************************************
|
||||
* DO NOT MANUALLY EDIT THIS FILE. THIS FILE IS AUTOMATICALLY GENERATED.
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Map of the previews components, values are functions which returns the promise of the component type, which will be displayed as preview in the ExampleViewer component.
|
||||
* Keys has to be equal to paths written down in the docs markdown files.
|
||||
*/
|
||||
export const PREVIEWS_COMPONENTS_MAP: Record<string, () => Promise<Type<unknown>>> = {};
|
||||
export const PREVIEWS_COMPONENTS_MAP: Record<string, () => Promise<Type<unknown>>> = {
|
||||
${previewsComponents}
|
||||
};
|
||||
@@ -21,7 +21,7 @@
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"outputPath": "dist/example-app",
|
||||
"index": "src/index.html",
|
||||
@@ -66,7 +66,7 @@
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "example-app:build:production"
|
||||
@@ -78,13 +78,13 @@
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"builder": "@angular/build:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "example-app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"builder": "@angular/build:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": ["zone.js", "zone.js/testing"],
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"zone.js": "~0.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.0.0",
|
||||
"@angular/build": "^19.0.0",
|
||||
"@angular/cli": "^19.0.0",
|
||||
"@angular/compiler-cli": "^19.0.0",
|
||||
"@types/jasmine": "~3.10.0",
|
||||
|
||||
@@ -10,12 +10,12 @@ import {RendererThis, Token, TokenizerThis, Tokens} from 'marked';
|
||||
|
||||
/** Enum of all available alert severities. */
|
||||
export enum AlertSeverityLevel {
|
||||
Note = 'NOTE',
|
||||
Tip = 'TIP',
|
||||
NOTE = 'NOTE',
|
||||
TIP = 'TIP',
|
||||
TODO = 'TODO',
|
||||
QUESTION = 'QUESTION',
|
||||
Summary = 'SUMMARY',
|
||||
TLDR = 'TLDR',
|
||||
SUMMARY = 'SUMMARY',
|
||||
TLDR = 'TL;DR',
|
||||
CRITICAL = 'CRITICAL',
|
||||
IMPORTANT = 'IMPORTANT',
|
||||
HELPFUL = 'HELPFUL',
|
||||
@@ -39,14 +39,14 @@ export const docsAlertExtension = {
|
||||
level: 'block' as const,
|
||||
tokenizer(this: TokenizerThis, src: string): DocsAlertToken | undefined {
|
||||
let match: DocsAlert | undefined;
|
||||
for (let level in AlertSeverityLevel) {
|
||||
for (const key of Object.keys(AlertSeverityLevel)) {
|
||||
// Capture group 1: all alert text content after the severity level
|
||||
const rule = new RegExp('^s*' + level + ': (.*?)\n(\n|$)', 's');
|
||||
const rule = new RegExp('^s*' + key + ': (.*?)\n(\n|$)', 's');
|
||||
const possibleMatch = rule.exec(src);
|
||||
|
||||
if (possibleMatch?.[1]) {
|
||||
match = {
|
||||
severityLevel: level,
|
||||
severityLevel: key,
|
||||
alert: possibleMatch,
|
||||
};
|
||||
}
|
||||
@@ -57,12 +57,11 @@ export const docsAlertExtension = {
|
||||
type: 'docs-alert',
|
||||
raw: match.alert[0],
|
||||
body: match.alert[1].trim(),
|
||||
severityLevel: match.severityLevel,
|
||||
severityLevel: match.severityLevel.toLowerCase(),
|
||||
tokens: [],
|
||||
};
|
||||
token.body = `**${
|
||||
token.severityLevel === AlertSeverityLevel.TLDR ? 'TL;DR' : token.severityLevel
|
||||
}:** ${token.body}`;
|
||||
|
||||
token.body = `**${AlertSeverityLevel[match.severityLevel as keyof typeof AlertSeverityLevel]}:** ${token.body}`;
|
||||
this.lexer.blockTokens(token.body, token.tokens);
|
||||
return token;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
Note: Use Note for ancillary/additional information that's not _essential_ to the main text.
|
||||
NOTE: Use Note for ancillary/additional information that's not _essential_ to the main text.
|
||||
This is a multiline note
|
||||
|
||||
Tip: Use Tip to call out a specific task/action users can perform, or a fact that plays directly into a task/action.
|
||||
TIP: Use Tip to call out a specific task/action users can perform, or a fact that plays directly into a task/action.
|
||||
|
||||
TODO: Use TODO for incomplete documentation that you plan to expand soon. You can also assign the TODO, e.g. TODO(emmatwersky): Text.
|
||||
|
||||
QUESTION: Use Question to pose a question to the reader, kind of like a mini-quiz that they should be able to answer.
|
||||
|
||||
Summary: Use Summary to provide a two- or three-sentence synopsis of the page or section content, so readers can figure out whether this is the right place for them.
|
||||
SUMMARY: Use Summary to provide a two- or three-sentence synopsis of the page or section content, so readers can figure out whether this is the right place for them.
|
||||
|
||||
TLDR: Use TLDR if you can provide the essential information about a page or section in a sentence or two.
|
||||
|
||||
|
||||
@@ -24,15 +24,10 @@ describe('markdown to html', () => {
|
||||
markdownDocument = JSDOM.fragment(await parseMarkdown(markdownContent, {}));
|
||||
});
|
||||
|
||||
for (let level in AlertSeverityLevel) {
|
||||
it(`should create a docs-alert for ${level}:`, () => {
|
||||
const noteEl = markdownDocument.querySelector(`.docs-alert-${level.toLowerCase()}`);
|
||||
// TLDR is written without a semi colon in the markdown, but is rendered
|
||||
// with a colon, as such we have to adjust our expectation here.
|
||||
if (level === AlertSeverityLevel.TLDR) {
|
||||
level = 'TL;DR';
|
||||
}
|
||||
expect(noteEl?.textContent?.trim()).toMatch(`^${level}:`);
|
||||
for (const [key, level] of Object.entries(AlertSeverityLevel)) {
|
||||
it(`should create a docs-alert for ${key}:`, () => {
|
||||
const noteEl = markdownDocument.querySelector(`.docs-alert-${key.toLowerCase()}`);
|
||||
expect(noteEl?.textContent?.trim()).toMatch(new RegExp(`^${level}:`));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"outputPath": "dist/first-app",
|
||||
"index": "src/index.html",
|
||||
@@ -76,7 +76,7 @@
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "first-app:build:production"
|
||||
@@ -88,7 +88,7 @@
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"builder": "@angular/build:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "first-app:build"
|
||||
}
|
||||
|
||||
+3821
-9261
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "NG_BUILD_PARALLEL_TS=0 ng serve",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development"
|
||||
},
|
||||
@@ -15,12 +15,12 @@
|
||||
"@angular/platform-browser": "^19.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.0"
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.0.0",
|
||||
"@angular/build": "^19.0.0",
|
||||
"@angular/cli": "^19.0.0",
|
||||
"@angular/compiler-cli": "^19.0.0",
|
||||
"typescript": "~5.6.3"
|
||||
"typescript": "~5.7.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ ng_module(
|
||||
],
|
||||
),
|
||||
deps = [
|
||||
"//adev/shared-docs/constants",
|
||||
"//adev/shared-docs/interfaces",
|
||||
"//adev/shared-docs/providers",
|
||||
"//adev/shared-docs/utils",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Injectable, inject, signal} from '@angular/core';
|
||||
import {Injectable, inject, linkedSignal, signal} from '@angular/core';
|
||||
import {NavigationItem} from '../interfaces/index';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@@ -19,11 +19,13 @@ export class NavigationState {
|
||||
private readonly _activeNavigationItem = signal<NavigationItem | null>(null);
|
||||
private readonly _expandedItems = signal<NavigationItem[]>([]);
|
||||
private readonly _isMobileNavVisible = signal<boolean>(false);
|
||||
private readonly _level = linkedSignal(() => this._expandedItems().length);
|
||||
|
||||
primaryActiveRouteItem = signal<string | null>(null);
|
||||
activeNavigationItem = this._activeNavigationItem.asReadonly();
|
||||
expandedItems = this._expandedItems.asReadonly();
|
||||
isMobileNavVisible = this._isMobileNavVisible.asReadonly();
|
||||
level = this._level.asReadonly();
|
||||
|
||||
async toggleItem(item: NavigationItem): Promise<void> {
|
||||
if (!item.children) {
|
||||
@@ -59,7 +61,7 @@ export class NavigationState {
|
||||
return;
|
||||
}
|
||||
// Returns item when parent node was already expanded
|
||||
const parentItem = this._expandedItems().find(
|
||||
const parentItem = this.actualExpandedItems().find(
|
||||
(expandedItem) =>
|
||||
item.parent?.label === expandedItem.label && item.parent?.path === expandedItem.path,
|
||||
);
|
||||
@@ -100,13 +102,25 @@ export class NavigationState {
|
||||
private expand(item: NavigationItem): void {
|
||||
// Add item to the expanded items list
|
||||
this._expandedItems.update((expandedItems) => {
|
||||
return [...(expandedItems ?? []), {...item, isExpanded: true}];
|
||||
return [...(this.actualExpandedItems() ?? []), {...item, isExpanded: true}];
|
||||
});
|
||||
|
||||
// No need to update the level here, this is handled by linkedSignal already
|
||||
}
|
||||
|
||||
private collapse(item: NavigationItem): void {
|
||||
item.isExpanded = false;
|
||||
this._expandedItems.update((expandedItems) => expandedItems.slice(0, -1));
|
||||
|
||||
// We won't remove the item, just update the level,
|
||||
// this allows animation on the items to hide them without destroying them
|
||||
this._level.set(this.actualExpandedItems().length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the actual navigation items, that is to say the one that match the current level
|
||||
*/
|
||||
private actualExpandedItems() {
|
||||
return this.expandedItems().slice(0, this.level());
|
||||
}
|
||||
|
||||
private async navigateToFirstPageOfTheCategory(path: string): Promise<boolean> {
|
||||
|
||||
@@ -6,95 +6,105 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Injectable, afterNextRender, inject, signal} from '@angular/core';
|
||||
import {ENVIRONMENT} from '../providers/index';
|
||||
import {SearchResult} from '../interfaces/index';
|
||||
import {toObservable} from '@angular/core/rxjs-interop';
|
||||
import {debounceTime, filter, from, of, switchMap} from 'rxjs';
|
||||
import {liteClient as algoliasearch} from 'algoliasearch/lite';
|
||||
import {
|
||||
DestroyRef,
|
||||
Injectable,
|
||||
InjectionToken,
|
||||
Provider,
|
||||
ResourceRef,
|
||||
inject,
|
||||
resource,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {NavigationEnd, Router} from '@angular/router';
|
||||
import {ENVIRONMENT} from '../providers/index';
|
||||
import type {Environment, SearchResult, SearchResultItem, SnippetResult} from '../interfaces/index';
|
||||
import {filter} from 'rxjs';
|
||||
import {
|
||||
liteClient as algoliasearch,
|
||||
SearchResponses,
|
||||
SearchResult as AlgoliaSearchResult,
|
||||
Hit,
|
||||
} from 'algoliasearch/lite';
|
||||
|
||||
export const SEARCH_DELAY = 200;
|
||||
// Maximum number of facet values to return for each facet during a regular search.
|
||||
export const MAX_VALUE_PER_FACET = 5;
|
||||
|
||||
export const ALGOLIA_CLIENT = new InjectionToken<ReturnType<typeof algoliasearch>>(
|
||||
'Search service',
|
||||
);
|
||||
|
||||
export const provideAlgoliaSearchClient = (config: Environment): Provider => {
|
||||
return {
|
||||
provide: ALGOLIA_CLIENT,
|
||||
useFactory: () => algoliasearch(config.algolia.appId, config.algolia.apiKey),
|
||||
};
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class Search {
|
||||
private readonly _searchQuery = signal('');
|
||||
private readonly _searchResults = signal<undefined | SearchResult[]>(undefined);
|
||||
readonly searchQuery = signal('');
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly router = inject(Router);
|
||||
private readonly config = inject(ENVIRONMENT);
|
||||
private readonly client = algoliasearch(this.config.algolia.appId, this.config.algolia.apiKey);
|
||||
private readonly client = inject(ALGOLIA_CLIENT);
|
||||
|
||||
searchQuery = this._searchQuery.asReadonly();
|
||||
searchResults = this._searchResults.asReadonly();
|
||||
searchResults = resource({
|
||||
request: () => this.searchQuery() || undefined, // coerces empty string to undefined
|
||||
loader: async ({request: query, abortSignal}) => {
|
||||
// Until we have a better alternative we debounce by awaiting for a short delay.
|
||||
await wait(SEARCH_DELAY, abortSignal);
|
||||
|
||||
searchResults$ = toObservable(this.searchQuery).pipe(
|
||||
debounceTime(SEARCH_DELAY),
|
||||
switchMap((query) => {
|
||||
return !!query
|
||||
? from(
|
||||
this.client.search([
|
||||
{
|
||||
indexName: this.config.algolia.indexName,
|
||||
params: {
|
||||
query: query,
|
||||
maxValuesPerFacet: MAX_VALUE_PER_FACET,
|
||||
attributesToRetrieve: [
|
||||
'hierarchy.lvl0',
|
||||
'hierarchy.lvl1',
|
||||
'hierarchy.lvl2',
|
||||
'hierarchy.lvl3',
|
||||
'hierarchy.lvl4',
|
||||
'hierarchy.lvl5',
|
||||
'hierarchy.lvl6',
|
||||
'content',
|
||||
'type',
|
||||
'url',
|
||||
],
|
||||
hitsPerPage: 20,
|
||||
snippetEllipsisText: '…',
|
||||
highlightPreTag: '<ɵ>',
|
||||
highlightPostTag: '</ɵ>',
|
||||
attributesToHighlight: [],
|
||||
attributesToSnippet: [
|
||||
'hierarchy.lvl1:10',
|
||||
'hierarchy.lvl2:10',
|
||||
'hierarchy.lvl3:10',
|
||||
'hierarchy.lvl4:10',
|
||||
'hierarchy.lvl5:10',
|
||||
'hierarchy.lvl6:10',
|
||||
'content:10',
|
||||
],
|
||||
},
|
||||
type: 'default',
|
||||
},
|
||||
]),
|
||||
)
|
||||
: of(undefined);
|
||||
}),
|
||||
);
|
||||
return this.client
|
||||
.search([
|
||||
{
|
||||
indexName: this.config.algolia.indexName,
|
||||
params: {
|
||||
query: query,
|
||||
maxValuesPerFacet: MAX_VALUE_PER_FACET,
|
||||
attributesToRetrieve: [
|
||||
'hierarchy.lvl0',
|
||||
'hierarchy.lvl1',
|
||||
'hierarchy.lvl2',
|
||||
'hierarchy.lvl3',
|
||||
'hierarchy.lvl4',
|
||||
'hierarchy.lvl5',
|
||||
'hierarchy.lvl6',
|
||||
'content',
|
||||
'type',
|
||||
'url',
|
||||
],
|
||||
hitsPerPage: 20,
|
||||
snippetEllipsisText: '…',
|
||||
highlightPreTag: '<ɵ>',
|
||||
highlightPostTag: '</ɵ>',
|
||||
attributesToHighlight: [],
|
||||
attributesToSnippet: [
|
||||
'hierarchy.lvl1:10',
|
||||
'hierarchy.lvl2:10',
|
||||
'hierarchy.lvl3:10',
|
||||
'hierarchy.lvl4:10',
|
||||
'hierarchy.lvl5:10',
|
||||
'hierarchy.lvl6:10',
|
||||
'content:10',
|
||||
],
|
||||
},
|
||||
type: 'default',
|
||||
},
|
||||
])
|
||||
.then((response: SearchResponses<unknown>) => {
|
||||
return this.parseResult(response);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
constructor() {
|
||||
afterNextRender(() => {
|
||||
this.listenToSearchResults();
|
||||
this.resetSearchQueryOnNavigationEnd();
|
||||
});
|
||||
}
|
||||
|
||||
updateSearchQuery(query: string): void {
|
||||
this._searchQuery.set(query);
|
||||
}
|
||||
|
||||
private listenToSearchResults(): void {
|
||||
this.searchResults$.subscribe((response: any) => {
|
||||
this._searchResults.set(
|
||||
response ? this.getUniqueSearchResultItems(response.results[0].hits) : undefined,
|
||||
);
|
||||
});
|
||||
this.resetSearchQueryOnNavigationEnd();
|
||||
}
|
||||
|
||||
private getUniqueSearchResultItems(items: SearchResult[]): SearchResult[] {
|
||||
@@ -115,8 +125,9 @@ export class Search {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (item.url && !uniqueUrls.has(item.url)) {
|
||||
uniqueUrls.add(item.url);
|
||||
|
||||
if (item['url'] && typeof item['url'] === 'string' && !uniqueUrls.has(item['url'])) {
|
||||
uniqueUrls.add(item['url']);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -124,8 +135,115 @@ export class Search {
|
||||
}
|
||||
|
||||
private resetSearchQueryOnNavigationEnd(): void {
|
||||
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
|
||||
this.updateSearchQuery('');
|
||||
this.router.events
|
||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(() => {
|
||||
this.searchQuery.set('');
|
||||
});
|
||||
}
|
||||
|
||||
private parseResult(response: SearchResponses<unknown>): SearchResultItem[] | undefined {
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result: AlgoliaSearchResult = response.results[0];
|
||||
if (!result || !('hits' in result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = result.hits as unknown as SearchResult[];
|
||||
|
||||
return this.getUniqueSearchResultItems(items).map((hitItem: SearchResult) => {
|
||||
const content = hitItem._snippetResult.content;
|
||||
const hierarchy = hitItem._snippetResult.hierarchy;
|
||||
const hasSubLabel = content || hierarchy?.lvl2 || hierarchy?.lvl3 || hierarchy?.lvl4;
|
||||
|
||||
return {
|
||||
id: hitItem.objectID,
|
||||
type: hitItem.hierarchy.lvl0 === 'Tutorials' ? 'code' : 'doc',
|
||||
url: hitItem.url,
|
||||
|
||||
labelHtml: this.parseLabelToHtml(hitItem._snippetResult.hierarchy?.lvl1?.value ?? ''),
|
||||
subLabelHtml: this.parseLabelToHtml(
|
||||
hasSubLabel ? this.getBestSnippetForMatch(hitItem) : null,
|
||||
),
|
||||
|
||||
category: hitItem.hierarchy?.lvl0 ?? null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getBestSnippetForMatch(result: SearchResult): string {
|
||||
// if there is content, return it
|
||||
if (result._snippetResult.content !== undefined) {
|
||||
return result._snippetResult.content.value;
|
||||
}
|
||||
|
||||
const hierarchy = result._snippetResult.hierarchy;
|
||||
if (hierarchy === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// return the most specific subheader match
|
||||
if (matched(hierarchy.lvl4)) {
|
||||
return hierarchy.lvl4!.value;
|
||||
}
|
||||
if (matched(hierarchy.lvl3)) {
|
||||
return hierarchy.lvl3!.value;
|
||||
}
|
||||
if (matched(hierarchy.lvl2)) {
|
||||
return hierarchy.lvl2!.value;
|
||||
}
|
||||
// if no subheader matched the query, fall back to just returning the most specific one
|
||||
return hierarchy.lvl3?.value ?? hierarchy.lvl2?.value ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML string with marked text for the matches
|
||||
*/
|
||||
private parseLabelToHtml(label: string | null): string | null {
|
||||
if (label === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parts: Array<{highlight: boolean; text: string}> = [];
|
||||
while (label.indexOf('<ɵ>') !== -1) {
|
||||
const beforeMatch = label.substring(0, label.indexOf('<ɵ>'));
|
||||
const match = label.substring(label.indexOf('<ɵ>') + 3, label.indexOf('</ɵ>'));
|
||||
parts.push({highlight: false, text: beforeMatch});
|
||||
parts.push({highlight: true, text: match});
|
||||
label = label.substring(label.indexOf('</ɵ>') + 4);
|
||||
}
|
||||
parts.push({highlight: false, text: label});
|
||||
|
||||
return parts
|
||||
.map((part) => {
|
||||
return part.highlight ? `<mark>${part.text}</mark>` : `<span>${part.text}</span>`;
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
}
|
||||
|
||||
function matched(snippet: SnippetResult | undefined): boolean {
|
||||
return snippet?.matchLevel !== undefined && snippet.matchLevel !== 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary helper to implement the debounce functionality on the search resource
|
||||
*/
|
||||
function wait(ms: number, signal: AbortSignal) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => resolve(), ms);
|
||||
|
||||
signal.addEventListener(
|
||||
'abort',
|
||||
() => {
|
||||
clearTimeout(timeout);
|
||||
reject(new Error('Operation aborted'));
|
||||
},
|
||||
{once: true},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {DOCUMENT, isPlatformBrowser} from '@angular/common';
|
||||
import {inject, signal, Injectable, PLATFORM_ID} from '@angular/core';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {inject, signal, Injectable, PLATFORM_ID, DestroyRef} from '@angular/core';
|
||||
|
||||
import {TableOfContentsItem, TableOfContentsLevel} from '../interfaces/index';
|
||||
import {on} from 'events';
|
||||
|
||||
/**
|
||||
* Name of an attribute that is set on an element that should be
|
||||
@@ -29,38 +30,19 @@ export class TableOfContentsLoader {
|
||||
readonly tableOfContentItems = signal([] as TableOfContentsItem[]);
|
||||
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly platformId = inject(PLATFORM_ID);
|
||||
|
||||
buildTableOfContent(docElement: Element): void {
|
||||
const headings = this.getHeadings(docElement);
|
||||
|
||||
const tocList: TableOfContentsItem[] = headings.map((heading) => ({
|
||||
id: heading.id,
|
||||
level: heading.tagName.toLowerCase() as TableOfContentsLevel,
|
||||
title: this.getHeadingTitle(heading),
|
||||
top: this.calculateTop(heading),
|
||||
}));
|
||||
|
||||
this.tableOfContentItems.set(tocList);
|
||||
}
|
||||
|
||||
// Update top value of heading, it should be executed after window resize
|
||||
updateHeadingsTopValue(element: HTMLElement): void {
|
||||
const headings = this.getHeadings(element);
|
||||
const updatedTopValues = new Map<string, number>();
|
||||
|
||||
for (const heading of headings) {
|
||||
updatedTopValues.set(heading.id, this.calculateTop(heading));
|
||||
}
|
||||
|
||||
this.tableOfContentItems.update((oldItems) => {
|
||||
let newItems = [...oldItems];
|
||||
for (const item of newItems) {
|
||||
item.top = updatedTopValues.get(item.id) ?? 0;
|
||||
}
|
||||
return newItems;
|
||||
});
|
||||
}
|
||||
|
||||
private getHeadingTitle(heading: HTMLHeadingElement): string {
|
||||
const div: HTMLDivElement = this.document.createElement('div');
|
||||
div.innerHTML = heading.innerHTML;
|
||||
@@ -79,11 +61,37 @@ export class TableOfContentsLoader {
|
||||
);
|
||||
}
|
||||
|
||||
private calculateTop(heading: HTMLHeadingElement): number {
|
||||
if (!isPlatformBrowser(this.platformId)) return 0;
|
||||
return (
|
||||
Math.floor(heading.offsetTop > 0 ? heading.offsetTop : heading.getClientRects()[0]?.top) -
|
||||
this.toleranceThreshold
|
||||
);
|
||||
/**
|
||||
* The methods setups several IntersectionObservers to determine when a heading is at the top of
|
||||
* the viewport. Using an IntersectionObserver is more efficient than reading DOM position
|
||||
* as it won't trigger any reflow.
|
||||
*/
|
||||
setupIntersectionObserver(
|
||||
element: Element,
|
||||
destroyRef: DestroyRef,
|
||||
onActiveId: (id: string) => void,
|
||||
): void {
|
||||
// If we're at the top the we need the default active id to be the first heading.
|
||||
const headings = this.getHeadings(element);
|
||||
onActiveId(headings[0].id);
|
||||
|
||||
headings.forEach((heading) => {
|
||||
const ioConfiguration = {
|
||||
/**
|
||||
* This rootMargin creates a horizontal line at 5% from the top of the viewport
|
||||
* that will help trigger an intersection at that the very point.
|
||||
*/
|
||||
rootMargin: '0% 0% -95% 0%',
|
||||
/** 0 is the default */
|
||||
threshold: 0,
|
||||
};
|
||||
const observer = new IntersectionObserver((entries, o) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
onActiveId(entries[0].target.id);
|
||||
}
|
||||
}, ioConfiguration);
|
||||
observer.observe(heading);
|
||||
destroyRef.onDestroy(() => observer.disconnect());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,153 +6,32 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {DOCUMENT, ViewportScroller} from '@angular/common';
|
||||
import {
|
||||
DestroyRef,
|
||||
EnvironmentInjector,
|
||||
Injectable,
|
||||
afterNextRender,
|
||||
inject,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import {RESIZE_EVENT_DELAY} from '../constants/index';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {auditTime, debounceTime, fromEvent, startWith} from 'rxjs';
|
||||
import {WINDOW} from '../providers/index';
|
||||
import {ViewportScroller} from '@angular/common';
|
||||
import {DestroyRef, Injectable, inject, signal} from '@angular/core';
|
||||
import {TableOfContentsLoader} from './table-of-contents-loader.service';
|
||||
|
||||
export const SCROLL_EVENT_DELAY = 20;
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
// The service is responsible for listening for scrolling and resizing,
|
||||
// thanks to which it sets the active item in the Table of contents
|
||||
export class TableOfContentsScrollSpy {
|
||||
private readonly tableOfContentsLoader = inject(TableOfContentsLoader);
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly window = inject(WINDOW);
|
||||
private readonly viewportScroller = inject(ViewportScroller);
|
||||
private readonly injector = inject(EnvironmentInjector);
|
||||
private contentSourceElement: HTMLElement | null = null;
|
||||
|
||||
private lastContentWidth = 0;
|
||||
|
||||
activeItemId = signal<string | null>(null);
|
||||
scrollbarThumbOnTop = signal<boolean>(true);
|
||||
|
||||
startListeningToScroll(
|
||||
contentSourceElement: HTMLElement | null,
|
||||
// `destroyRef` is required because the caller may invoke `startListeningToScroll`
|
||||
// multiple times. Without it, previous event listeners would not be disposed of,
|
||||
// leading to the accumulation of new event listeners.
|
||||
destroyRef: DestroyRef,
|
||||
) {
|
||||
this.contentSourceElement = contentSourceElement;
|
||||
this.lastContentWidth = this.getContentWidth();
|
||||
|
||||
this.setScrollEventHandlers(destroyRef);
|
||||
this.setResizeEventHandlers(destroyRef);
|
||||
|
||||
destroyRef.onDestroy(() => {
|
||||
// We also need to clean up the source element once the view that calls
|
||||
// `startListeningToScroll` is destroyed, as this will keep a reference
|
||||
// to an element that has been removed from the DOM.
|
||||
this.contentSourceElement = null;
|
||||
});
|
||||
}
|
||||
|
||||
scrollToTop(): void {
|
||||
this.viewportScroller.scrollToPosition([0, 0]);
|
||||
}
|
||||
|
||||
// After window resize, we should update top value of each table content item
|
||||
private setResizeEventHandlers(destroyRef: DestroyRef) {
|
||||
fromEvent(this.window, 'resize')
|
||||
.pipe(debounceTime(RESIZE_EVENT_DELAY), takeUntilDestroyed(destroyRef), startWith())
|
||||
.subscribe(() => {
|
||||
this.updateHeadingsTopAfterResize();
|
||||
});
|
||||
|
||||
// We need to observe the height of the docs-viewer because it may change after the
|
||||
// assets (fonts, images) are loaded. They can (and will) change the y-position of the headings.
|
||||
const docsViewer = this.document.querySelector('docs-viewer');
|
||||
if (docsViewer) {
|
||||
const ref = afterNextRender(
|
||||
() => {
|
||||
const resizeObserver = new ResizeObserver(() => this.updateHeadingsTopAfterResize());
|
||||
resizeObserver.observe(docsViewer);
|
||||
destroyRef.onDestroy(() => resizeObserver.disconnect());
|
||||
setupActiveItemListener(contentSourceElement: HTMLElement, destroyRef: DestroyRef): void {
|
||||
if (contentSourceElement) {
|
||||
this.tableOfContentsLoader.setupIntersectionObserver(
|
||||
contentSourceElement,
|
||||
destroyRef,
|
||||
(id) => {
|
||||
this.activeItemId.set(id);
|
||||
},
|
||||
{injector: this.injector, manualCleanup: true},
|
||||
);
|
||||
|
||||
destroyRef.onDestroy(() => ref.destroy());
|
||||
}
|
||||
}
|
||||
|
||||
private updateHeadingsTopAfterResize(): void {
|
||||
this.lastContentWidth = this.getContentWidth();
|
||||
|
||||
const contentElement = this.contentSourceElement;
|
||||
if (contentElement) {
|
||||
this.tableOfContentsLoader.updateHeadingsTopValue(contentElement);
|
||||
this.setActiveItemId();
|
||||
}
|
||||
}
|
||||
|
||||
private setScrollEventHandlers(destroyRef: DestroyRef): void {
|
||||
const scroll$ = fromEvent(this.document, 'scroll').pipe(
|
||||
auditTime(SCROLL_EVENT_DELAY),
|
||||
takeUntilDestroyed(destroyRef),
|
||||
);
|
||||
|
||||
scroll$.subscribe(() => this.setActiveItemId());
|
||||
}
|
||||
|
||||
private setActiveItemId(): void {
|
||||
const tableOfContentItems = this.tableOfContentsLoader.tableOfContentItems();
|
||||
|
||||
if (tableOfContentItems.length === 0) return;
|
||||
|
||||
// Resize could emit scroll event, in that case we could stop setting active item until resize will be finished
|
||||
if (this.lastContentWidth !== this.getContentWidth()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollOffset = this.getScrollOffset();
|
||||
if (scrollOffset === null) return;
|
||||
|
||||
for (const [i, currentLink] of tableOfContentItems.entries()) {
|
||||
const nextLink = tableOfContentItems[i + 1];
|
||||
|
||||
// A link is considered active if the page is scrolled past the
|
||||
// anchor without also being scrolled passed the next link.
|
||||
const isActive =
|
||||
scrollOffset >= currentLink.top && (!nextLink || nextLink.top >= scrollOffset);
|
||||
|
||||
// When active item was changed then trigger change detection
|
||||
if (isActive && this.activeItemId() !== currentLink.id) {
|
||||
this.activeItemId.set(currentLink.id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (scrollOffset < tableOfContentItems[0].top && this.activeItemId() !== null) {
|
||||
this.activeItemId.set(null);
|
||||
}
|
||||
|
||||
const scrollOffsetZero = scrollOffset === 0;
|
||||
if (scrollOffsetZero !== this.scrollbarThumbOnTop()) {
|
||||
// we want to trigger change detection only when the value changes
|
||||
this.scrollbarThumbOnTop.set(scrollOffsetZero);
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the scroll offset of the scroll container
|
||||
private getScrollOffset(): number {
|
||||
return this.window.scrollY;
|
||||
}
|
||||
|
||||
private getContentWidth(): number {
|
||||
return this.document.body.clientWidth || Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,10 @@
|
||||
--label-theme: var(--symbolic-teal);
|
||||
}
|
||||
|
||||
&.type-impure-pipe {
|
||||
--label-theme: var(--symbolic-pink);
|
||||
}
|
||||
|
||||
&.type-ng_module {
|
||||
--label-theme: var(--symbolic-brown);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
// Colors
|
||||
// Using OKLCH color space for better color reproduction on P3 displays,
|
||||
// as well as better human-readability
|
||||
@@ -186,9 +188,6 @@
|
||||
// for the "unfilled" portion of the word that hasn't
|
||||
// been highlighted by the gradient
|
||||
--gray-unfilled: var(--gray-400);
|
||||
// TODO: convert oklch to hex at build time
|
||||
--webgl-page-background: #ffffff;
|
||||
--webgl-gray-unfilled: #a39fa9;
|
||||
}
|
||||
|
||||
@mixin dark-mode-definitions() {
|
||||
@@ -254,9 +253,6 @@
|
||||
|
||||
// Home page - dark mode
|
||||
--gray-unfilled: var(--gray-700);
|
||||
// TODO: convert oklch to hex at build time
|
||||
--webgl-page-background: #0f0f11;
|
||||
--webgl-gray-unfilled: #413e46;
|
||||
|
||||
.docs-toggle {
|
||||
input {
|
||||
@@ -268,9 +264,13 @@
|
||||
}
|
||||
|
||||
@mixin mdc-definitions() {
|
||||
--mdc-snackbar-container-shape: 0.25rem;
|
||||
--mdc-snackbar-container-color: var(--page-background);
|
||||
--mdc-snackbar-supporting-text-color: var(--primary-contrast);
|
||||
@include mat.snack-bar-overrides(
|
||||
(
|
||||
container-shape: 0.25rem,
|
||||
container-color: var(--page-background),
|
||||
supporting-text-color: var(--primary-contrast),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// LIGHT MODE (Explicit)
|
||||
|
||||
@@ -9,38 +9,39 @@ $screen-xl: 1800px;
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-tablet-portrait-up {
|
||||
@media (min-width: $screen-xs) {
|
||||
@media (min-width: calc($screen-xs + 1px)) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-tablet {
|
||||
@media (min-width: $screen-xs) and (max-width: $screen-md) {
|
||||
@media (min-width: calc($screen-xs + 1px)) and (max-width: $screen-md) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-tablet-up {
|
||||
@media (min-width: $screen-sm) {
|
||||
@media (min-width: calc($screen-sm + 1px)) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-tablet-landscape-up {
|
||||
@media (min-width: $screen-md) {
|
||||
@media (min-width: calc($screen-md + 1px)) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-desktop-up {
|
||||
@media (min-width: $screen-lg) {
|
||||
@media (min-width: calc($screen-lg + 1px)) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin for-big-desktop-up {
|
||||
@media (min-width: $screen-xl) {
|
||||
@media (min-width: calc($screen-xl + 1px)) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,11 @@
|
||||
.docs-reference-section-heading {
|
||||
padding-block-start: 3rem;
|
||||
|
||||
a {
|
||||
&--sub {
|
||||
padding-block-start: 1rem;
|
||||
}
|
||||
|
||||
& > a {
|
||||
@include anchor.docs-anchor();
|
||||
color: inherit;
|
||||
}
|
||||
@@ -98,7 +102,7 @@
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&.highlighted {
|
||||
&.docs-highlighted-card {
|
||||
box-shadow: 10px 4px 40px 0 rgba(0, 0, 0, 0.01);
|
||||
|
||||
&::before {
|
||||
@@ -255,6 +259,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.pipe-usage {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
// "API member card"-specific styles
|
||||
.docs-reference-member-card {
|
||||
.docs-reference-card-item {
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
@mixin resets() {
|
||||
:root {
|
||||
--fallback-font-stack: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--fallback-font-stack:
|
||||
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--page-width: 80ch;
|
||||
--layout-padding: 3.12rem; // a common padding value throughout the layout
|
||||
--primary-nav-width: 110px;
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
}
|
||||
|
||||
p > a,
|
||||
p > em > a,
|
||||
td > a,
|
||||
div > a:not(.docs-card),
|
||||
code > a,
|
||||
@@ -93,6 +94,7 @@
|
||||
}
|
||||
|
||||
p > a,
|
||||
p > em > a,
|
||||
.docs-list a,
|
||||
.docs-card a {
|
||||
margin-block: 0;
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
fill: var(--page-background) !important;
|
||||
}
|
||||
|
||||
.nodeLabel:not(.node:has(polygon) .nodeLabel) {
|
||||
.nodeLabel:not(.node:has(polygon) .nodeLabel):not(.eventNode .nodeLabel) {
|
||||
fill: var(--primary-contrast) !important;
|
||||
color: var(--primary-contrast) !important;
|
||||
}
|
||||
@@ -71,7 +71,6 @@
|
||||
}
|
||||
|
||||
.nodeLabel p {
|
||||
color: var(--page-background) !important;
|
||||
font-weight: 800 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,33 +85,29 @@ export class FakeWebContainer extends WebContainer {
|
||||
if (fakeOptions?.spawn) this.fakeSpawn = fakeOptions.spawn;
|
||||
}
|
||||
|
||||
override spawn(
|
||||
override async spawn(
|
||||
command: unknown,
|
||||
args?: unknown,
|
||||
options?: unknown,
|
||||
): Promise<FakeWebContainerProcess> {
|
||||
if (this.fakeSpawn) return Promise.resolve(this.fakeSpawn);
|
||||
if (this.fakeSpawn) return this.fakeSpawn;
|
||||
|
||||
const fakeProcess = new FakeWebContainerProcess();
|
||||
|
||||
return Promise.resolve(fakeProcess);
|
||||
return new FakeWebContainerProcess();
|
||||
}
|
||||
override on(event: 'port', listener: PortListener): Unsubscribe;
|
||||
override on(event: 'server-ready', listener: ServerReadyListener): Unsubscribe;
|
||||
override on(event: 'error', listener: ErrorListener): Unsubscribe;
|
||||
override on(event: 'preview-message', listener: PreviewMessageListener): Unsubscribe;
|
||||
|
||||
override on(event: unknown, listener: unknown): Unsubscribe {
|
||||
return () => {};
|
||||
}
|
||||
override mount(
|
||||
|
||||
override async mount(
|
||||
tree: FileSystemTree,
|
||||
options?: {mountPoint?: string | undefined} | undefined,
|
||||
): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
): Promise<void> {}
|
||||
|
||||
override get path() {
|
||||
return '/fake-path';
|
||||
}
|
||||
|
||||
override get workdir() {
|
||||
return '/fake-workdir';
|
||||
}
|
||||
@@ -142,19 +138,15 @@ class FakeFileSystemAPI implements FileSystemAPI {
|
||||
path: string,
|
||||
options: {encoding?: string | null | undefined; withFileTypes: true},
|
||||
): Promise<DirEnt<string>[]>;
|
||||
readdir(
|
||||
async readdir(
|
||||
path: unknown,
|
||||
options?: {encoding?: string | null | undefined; withFileTypes?: boolean} | string | null,
|
||||
):
|
||||
| Promise<Uint8Array[]>
|
||||
| Promise<string[]>
|
||||
| Promise<DirEnt<Uint8Array>[]>
|
||||
| Promise<DirEnt<string>[]> {
|
||||
): Promise<Uint8Array[] | string[] | DirEnt<Uint8Array>[] | DirEnt<string>[]> {
|
||||
if (typeof options === 'object' && options?.withFileTypes === true) {
|
||||
return Promise.resolve([{name: 'fake-file', isFile: () => true, isDirectory: () => false}]);
|
||||
return [{name: 'fake-file', isFile: () => true, isDirectory: () => false}];
|
||||
}
|
||||
|
||||
return Promise.resolve(['/fake-dirname']);
|
||||
return ['/fake-dirname'];
|
||||
}
|
||||
|
||||
readFile(path: string, encoding?: null | undefined): Promise<Uint8Array>;
|
||||
@@ -162,28 +154,25 @@ class FakeFileSystemAPI implements FileSystemAPI {
|
||||
readFile(path: unknown, encoding?: unknown): Promise<Uint8Array> | Promise<string> {
|
||||
return Promise.resolve('fake file content');
|
||||
}
|
||||
writeFile(
|
||||
async writeFile(
|
||||
path: string,
|
||||
data: string | Uint8Array,
|
||||
options?: string | {encoding?: string | null | undefined} | null | undefined,
|
||||
): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
): Promise<void> {}
|
||||
|
||||
mkdir(path: string, options?: {recursive?: false | undefined} | undefined): Promise<void>;
|
||||
mkdir(path: string, options: {recursive: true}): Promise<string>;
|
||||
mkdir(path: unknown, options?: unknown): Promise<void> | Promise<string> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
rm(
|
||||
async mkdir(path: unknown, options?: unknown): Promise<void | string> {}
|
||||
|
||||
async rm(
|
||||
path: string,
|
||||
options?: {force?: boolean | undefined; recursive?: boolean | undefined} | undefined,
|
||||
): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
): Promise<void> {}
|
||||
|
||||
rename(oldPath: string, newPath: string): Promise<void> {
|
||||
throw Error('Not implemented');
|
||||
}
|
||||
|
||||
watch(
|
||||
filename: string,
|
||||
options?: FSWatchOptions | undefined,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
gtag: (...args: any[]) => void;
|
||||
gtag?(...args: any[]): void;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@use '@angular/docs/styles/media-queries' as mq;
|
||||
|
||||
:host {
|
||||
max-width: 2560px;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -21,7 +20,7 @@
|
||||
}
|
||||
|
||||
// show it under the navbar.
|
||||
@include mq.for-tablet-landscape-down{
|
||||
@include mq.for-tablet-landscape-down {
|
||||
z-index: calc(var(--z-index-nav) - 1);
|
||||
}
|
||||
}
|
||||
@@ -96,6 +95,7 @@
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
max-width: 2560px;
|
||||
|
||||
// If navs are open, render a blurry background over content
|
||||
::after {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {AppComponent} from './app.component';
|
||||
import {provideRouter} from '@angular/router';
|
||||
import {provideRouter, withComponentInputBinding} from '@angular/router';
|
||||
import {routes} from './routes';
|
||||
import {Search, WINDOW} from '@angular/docs';
|
||||
import {CURRENT_MAJOR_VERSION} from './core/providers/current-version';
|
||||
@@ -21,7 +21,7 @@ describe('AppComponent', () => {
|
||||
it('should create the app', () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
provideRouter(routes),
|
||||
provideRouter(routes, withComponentInputBinding()),
|
||||
{
|
||||
provide: WINDOW,
|
||||
useValue: fakeWindow,
|
||||
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
inject,
|
||||
OnInit,
|
||||
PLATFORM_ID,
|
||||
input,
|
||||
signal,
|
||||
WritableSignal,
|
||||
} from '@angular/core';
|
||||
import {NavigationEnd, NavigationSkipped, Router, RouterOutlet} from '@angular/router';
|
||||
import {filter, map, skip} from 'rxjs/operators';
|
||||
import {filter, map} from 'rxjs/operators';
|
||||
import {
|
||||
CookiePopup,
|
||||
getActivatedRouteSnapshotFromRouter,
|
||||
@@ -56,13 +56,12 @@ export class AppComponent implements OnInit {
|
||||
private readonly router = inject(Router);
|
||||
private readonly headerService = inject(HeaderService);
|
||||
|
||||
currentUrl = signal('');
|
||||
displayFooter = signal(false);
|
||||
displaySecondaryNav = signal(false);
|
||||
displaySearchDialog: WritableSignal<boolean> = inject(IS_SEARCH_DIALOG_OPEN);
|
||||
|
||||
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
||||
|
||||
displaySecondaryNav = signal(false);
|
||||
displayFooter = signal(false);
|
||||
displaySearchDialog = inject(IS_SEARCH_DIALOG_OPEN);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.closeSearchDialogOnNavigationSkipped();
|
||||
this.router.events
|
||||
@@ -71,35 +70,24 @@ export class AppComponent implements OnInit {
|
||||
map((event) => event.urlAfterRedirects),
|
||||
)
|
||||
.subscribe((url) => {
|
||||
this.currentUrl.set(url);
|
||||
this.setComponentsVisibility();
|
||||
this.displaySearchDialog.set(false);
|
||||
// We can't use an input binded to the route here
|
||||
// because AppComponent itself is not a routed component
|
||||
// so we access it via the snapshot
|
||||
const activatedRoute = getActivatedRouteSnapshotFromRouter(this.router);
|
||||
this.displayFooter.set(!activatedRoute.data['hideFooter']);
|
||||
this.displaySecondaryNav.set(activatedRoute.data['displaySecondaryNav']);
|
||||
|
||||
this.displaySearchDialog.set(false);
|
||||
this.updateCanonicalLink(url);
|
||||
});
|
||||
}
|
||||
|
||||
focusFirstHeading(): void {
|
||||
if (!this.isBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const h1 = this.document.querySelector<HTMLHeadingElement>('h1:not(docs-top-level-banner h1)');
|
||||
h1?.focus();
|
||||
}
|
||||
|
||||
private updateCanonicalLink(absoluteUrl: string) {
|
||||
this.headerService.setCanonical(absoluteUrl);
|
||||
}
|
||||
|
||||
private setComponentsVisibility(): void {
|
||||
const activatedRoute = getActivatedRouteSnapshotFromRouter(this.router as any);
|
||||
|
||||
this.displaySecondaryNav.set(activatedRoute.data['displaySecondaryNav']);
|
||||
this.displayFooter.set(!activatedRoute.data['hideFooter']);
|
||||
}
|
||||
|
||||
private setSearchDialogVisibilityOnKeyPress(event: KeyboardEvent): void {
|
||||
protected setSearchDialogVisibilityOnKeyPress(event: KeyboardEvent): void {
|
||||
if (event.key === SEARCH_TRIGGER_KEY && (event.metaKey || event.ctrlKey)) {
|
||||
event.preventDefault();
|
||||
this.displaySearchDialog.update((display) => !display);
|
||||
@@ -111,6 +99,10 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private updateCanonicalLink(absoluteUrl: string) {
|
||||
this.headerService.setCanonical(absoluteUrl);
|
||||
}
|
||||
|
||||
private closeSearchDialogOnNavigationSkipped(): void {
|
||||
this.router.events.pipe(filter((event) => event instanceof NavigationSkipped)).subscribe(() => {
|
||||
this.displaySearchDialog.set(false);
|
||||
|
||||
@@ -21,11 +21,11 @@ import {
|
||||
ENVIRONMENT,
|
||||
EXAMPLE_VIEWER_CONTENT_LOADER,
|
||||
PREVIEWS_COMPONENTS,
|
||||
provideAlgoliaSearchClient,
|
||||
WINDOW,
|
||||
windowProvider,
|
||||
} from '@angular/docs';
|
||||
import {provideClientHydration} from '@angular/platform-browser';
|
||||
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
|
||||
import {
|
||||
RouteReuseStrategy,
|
||||
Router,
|
||||
@@ -75,9 +75,9 @@ export const appConfig: ApplicationConfig = {
|
||||
provideExperimentalZonelessChangeDetection(),
|
||||
provideClientHydration(),
|
||||
provideHttpClient(withFetch()),
|
||||
provideAnimationsAsync(),
|
||||
provideEnvironmentInitializer(() => inject(AppScroller)),
|
||||
provideEnvironmentInitializer(() => inject(AnalyticsService)),
|
||||
provideAlgoliaSearchClient(environment),
|
||||
{
|
||||
provide: CURRENT_MAJOR_VERSION,
|
||||
useValue: Number(VERSION.major),
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="docs-license">
|
||||
Super-powered by Google ©2010-2024. Code licensed under an
|
||||
Super-powered by Google ©2010-2025. Code licensed under an
|
||||
<a routerLink="/license" title="License text">MIT-style License</a>
|
||||
. Documentation licensed under
|
||||
<a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!-- Primary Nav -->
|
||||
<div
|
||||
[attr.id]="PRIMARY_NAV_ID"
|
||||
class="wrapper"
|
||||
(docsClickOutside)="closeMobileNav()"
|
||||
[docsClickOutsideIgnore]="[SECONDARY_NAV_ID]"
|
||||
>
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
z-index: 50;
|
||||
}
|
||||
}
|
||||
|
||||
@include mq.for-tablet-landscape-down {
|
||||
width: 100%;
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adev-mobile-nav-bar {
|
||||
@@ -27,7 +35,7 @@
|
||||
backdrop-filter: blur(16px);
|
||||
background-color: color-mix(in srgb, var(--page-background) 70%, transparent);
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
width: 100%;
|
||||
padding-block: 0.75rem;
|
||||
padding-inline: var(--layout-padding);
|
||||
border-block-end: 1px solid var(--septenary-contrast);
|
||||
@@ -74,7 +82,7 @@
|
||||
// Add a subtle border for the home page
|
||||
border-block-end: 1px solid var(--septenary-contrast);
|
||||
|
||||
@include mq.for-tablet-up {
|
||||
@include mq.for-tablet-landscape-up {
|
||||
border-inline-end: 1px solid var(--septenary-contrast);
|
||||
}
|
||||
|
||||
@@ -84,7 +92,7 @@
|
||||
|
||||
@include mq.for-tablet {
|
||||
flex-direction: row;
|
||||
width: 100vw;
|
||||
width: 100%;
|
||||
padding-inline: calc(var(--layout-padding) - 1.25rem);
|
||||
height: auto;
|
||||
padding-block: 0;
|
||||
@@ -293,6 +301,7 @@
|
||||
transition:
|
||||
background-color 0.3s ease,
|
||||
border-color 0.3s ease;
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
@@ -301,6 +310,7 @@
|
||||
padding: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@include mq.for-tablet-landscape-up {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ describe('ProgressBarComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should call progressBar.complete() on route change', async () => {
|
||||
const progressBar = component.progressBar();
|
||||
const progressBarCompleteSpy = spyOn(progressBar, 'complete');
|
||||
// This test often timeouts
|
||||
// We suspect a racing condition inside the RouterTestingHarness.
|
||||
// Until this has been investigated, we will skip this test.
|
||||
xit('should call progressBar.complete() on route change', async () => {
|
||||
const progressBarCompleteSpy = spyOn(component.progressBar(), 'complete');
|
||||
|
||||
const harness = await RouterTestingHarness.create();
|
||||
await harness.navigateByUrl('/');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user