docs(docs-infra): Add tests for marked rendering (#57344)

On top of #57338, to make sure we prevent similar regressions on marked rendering.

PR Close #57344
This commit is contained in:
Matthieu Riegler 2024-08-12 11:48:55 +02:00 committed by Dylan Hunn
parent a2990757cc
commit b87bbf6c95
6 changed files with 671 additions and 56 deletions

View file

@ -19,10 +19,18 @@ ts_library(
),
deps = [
"//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html_lib",
"@npm//@bazel/runfiles",
"@npm//@types/jsdom",
"@npm//jsdom",
],
)
jasmine_node_test(
name = "unit_tests",
data = [
"@npm//jsdom",
] + glob([
"**/*.json",
]),
deps = [":unit_test_lib"],
)

View file

@ -3,81 +3,624 @@
"entries": [
{
"name": "NgTemplateOutlet",
"description": "*one* directive",
"isAbstract": false,
"entryType": "directive",
"members": [],
"jsdocTags": [],
"rawComment": "",
"source": {
"fileName": "/packages/core/src/ng_template_outlet.ts",
"line": 7,
"character": 7
}
},
{
"name": "UserProfile",
"entryType": "undecorated_class",
"members": [
{
"name": "userId",
"type": "number",
"name": "ngTemplateOutletContext",
"type": "C",
"memberType": "property",
"memberTags": [],
"description": "A user identifier.",
"jsdocTags": []
"memberTags": ["input"],
"description": "A context object to attach to the {@link EmbeddedViewRef}. This should be an\nobject, the object's keys will be available for binding by the local template `let`\ndeclarations.\nUsing the key `$implicit` in the context object will set its value as default.",
"jsdocTags": [],
"inputAlias": "ngTemplateOutletContext",
"isRequiredInput": false
},
{
"name": "name",
"type": "string",
"memberType": "getter",
"memberTags": [],
"description": "Name of the user",
"jsdocTags": []
"name": "ngTemplateOutlet",
"type": "TemplateRef<C>",
"memberType": "property",
"memberTags": ["input"],
"description": "A string defining the template reference and optionally the context object for the template.",
"jsdocTags": [],
"inputAlias": "ngTemplateOutlet",
"isRequiredInput": false
},
{
"name": "name",
"type": "string",
"memberType": "setter",
"memberTags": [],
"description": "Name of the user",
"jsdocTags": []
"name": "ngTemplateOutletInjector",
"type": "Injector",
"memberType": "property",
"memberTags": ["input"],
"description": "Injector to be used within the embedded view.",
"jsdocTags": [],
"inputAlias": "ngTemplateOutletInjector",
"isRequiredInput": false
},
{
"params": [
"name": "ngOnChanges",
"signatures": [
{
"name": "config",
"description": "Setting for saving.",
"type": "object",
"isOptional": false,
"isRestParam": false
"name": "ngOnChanges",
"entryType": "function",
"description": "",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [
{
"name": "changes",
"description": "",
"type": "SimpleChanges",
"isOptional": false,
"isRestParam": false
}
],
"rawComment": "",
"returnType": "void"
}
],
"name": "save",
"returnType": "boolean",
"implementation": {
"params": [
{
"name": "changes",
"description": "",
"type": "SimpleChanges",
"isOptional": false,
"isRestParam": false
}
],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "ngOnChanges",
"description": "",
"entryType": "function",
"jsdocTags": [],
"rawComment": ""
},
"entryType": "function",
"description": "Save the user.",
"jsdocTags": [
{
"name": "param",
"comment": "Setting for saving."
},
{
"name": "returns",
"comment": "Whether it succeeded"
}
],
"rawComment": "/**\n * Save the user.\n * @param config Setting for saving.\n * @returns Whether it succeeded\n */",
"description": "",
"jsdocTags": [],
"rawComment": "",
"memberType": "method",
"memberTags": []
}
],
"generics": [{"name": "C", "default": "unknown"}],
"description": "",
"jsdocTags": [
{"name": "ngModule", "comment": "CommonModule"},
{
"name": "description",
"comment": "Inserts an embedded view from a prepared `TemplateRef`.\n\nYou can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`.\n`[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding\nby the local template `let` declarations."
},
{
"name": "usageNotes",
"comment": "```\n<ng-container *ngTemplateOutlet=\"templateRefExp; context: contextExp\"></ng-container>\n```\n\nUsing the key `$implicit` in the context object will set its value as default.\n\n### Example\n\n{@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}"
},
{"name": "publicApi", "comment": ""}
],
"rawComment": "/**\n * @ngModule CommonModule\n *\n * @description\n *\n * Inserts an embedded view from a prepared `TemplateRef`.\n *\n * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`.\n * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding\n * by the local template `let` declarations.\n *\n * @usageNotes\n * ```\n * <ng-container *ngTemplateOutlet=\"templateRefExp; context: contextExp\"></ng-container>\n * ```\n *\n * Using the key `$implicit` in the context object will set its value as default.\n *\n * ### Example\n *\n * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}\n *\n * @publicApi\n */",
"isStandalone": true,
"selector": "[ngTemplateOutlet]",
"exportAs": [],
"source": {
"filePath": "/packages/common/src/directives/ng_template_outlet.ts",
"startLine": 45,
"endLine": 126
}
},
{
"name": "ViewRef",
"isAbstract": true,
"entryType": "undecorated_class",
"members": [
{
"name": "destroy",
"signatures": [
{
"name": "destroy",
"entryType": "function",
"description": "Destroys this view and all of the data structures associated with it.",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "/**\n * Destroys this view and all of the data structures associated with it.\n */",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "destroy",
"description": "Destroys this view and all of the data structures associated with it.",
"entryType": "function",
"jsdocTags": [],
"rawComment": "/**\n * Destroys this view and all of the data structures associated with it.\n */"
},
"entryType": "function",
"description": "Destroys this view and all of the data structures associated with it.",
"jsdocTags": [],
"rawComment": "/**\n * Destroys this view and all of the data structures associated with it.\n */",
"memberType": "method",
"memberTags": ["abstract"]
},
{
"name": "destroyed",
"type": "boolean",
"memberType": "getter",
"memberTags": ["abstract"],
"description": "Reports whether this view has been destroyed.",
"jsdocTags": [
{
"name": "returns",
"comment": "True after the `destroy()` method has been called, false otherwise."
}
]
},
{
"name": "onDestroy",
"signatures": [
{
"name": "onDestroy",
"entryType": "function",
"description": "A lifecycle hook that provides additional developer-defined cleanup\nfunctionality for views.",
"generics": [],
"isNewType": false,
"jsdocTags": [
{
"name": "param",
"comment": "A handler function that cleans up developer-defined data\nassociated with a view. Called when the `destroy()` method is invoked."
}
],
"params": [
{
"name": "callback",
"description": "A handler function that cleans up developer-defined data\nassociated with a view. Called when the `destroy()` method is invoked.",
"type": "Function",
"isOptional": false,
"isRestParam": false
}
],
"rawComment": "/**\n * A lifecycle hook that provides additional developer-defined cleanup\n * functionality for views.\n * @param callback A handler function that cleans up developer-defined data\n * associated with a view. Called when the `destroy()` method is invoked.\n */",
"returnType": "void"
}
],
"implementation": {
"params": [
{
"name": "callback",
"description": "A handler function that cleans up developer-defined data\nassociated with a view. Called when the `destroy()` method is invoked.",
"type": "Function",
"isOptional": false,
"isRestParam": false
}
],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "onDestroy",
"description": "A lifecycle hook that provides additional developer-defined cleanup\nfunctionality for views.",
"entryType": "function",
"jsdocTags": [
{
"name": "param",
"comment": "A handler function that cleans up developer-defined data\nassociated with a view. Called when the `destroy()` method is invoked."
}
],
"rawComment": "/**\n * A lifecycle hook that provides additional developer-defined cleanup\n * functionality for views.\n * @param callback A handler function that cleans up developer-defined data\n * associated with a view. Called when the `destroy()` method is invoked.\n */"
},
"entryType": "function",
"description": "A lifecycle hook that provides additional developer-defined cleanup\nfunctionality for views.",
"jsdocTags": [
{
"name": "param",
"comment": "A handler function that cleans up developer-defined data\nassociated with a view. Called when the `destroy()` method is invoked."
}
],
"rawComment": "/**\n * A lifecycle hook that provides additional developer-defined cleanup\n * functionality for views.\n * @param callback A handler function that cleans up developer-defined data\n * associated with a view. Called when the `destroy()` method is invoked.\n */",
"memberType": "method",
"memberTags": ["abstract"]
},
{
"name": "markForCheck",
"signatures": [
{
"name": "markForCheck",
"entryType": "function",
"description": "When a view uses the {@link ChangeDetectionStrategy#OnPush} (checkOnce)\nchange detection strategy, explicitly marks the view as changed so that\nit can be checked again.\n\nComponents are normally marked as dirty (in need of rerendering) when inputs\nhave changed or events have fired in the view. Call this method to ensure that\na component is checked even if these triggers have not occurred.\n\n<!-- TODO: Add a link to a chapter on OnPush components -->",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "/**\n * When a view uses the {@link ChangeDetectionStrategy#OnPush} (checkOnce)\n * change detection strategy, explicitly marks the view as changed so that\n * it can be checked again.\n *\n * Components are normally marked as dirty (in need of rerendering) when inputs\n * have changed or events have fired in the view. Call this method to ensure that\n * a component is checked even if these triggers have not occurred.\n *\n * <!-- TODO: Add a link to a chapter on OnPush components -->\n *\n */",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "markForCheck",
"description": "When a view uses the {@link ChangeDetectionStrategy#OnPush} (checkOnce)\nchange detection strategy, explicitly marks the view as changed so that\nit can be checked again.\n\nComponents are normally marked as dirty (in need of rerendering) when inputs\nhave changed or events have fired in the view. Call this method to ensure that\na component is checked even if these triggers have not occurred.\n\n<!-- TODO: Add a link to a chapter on OnPush components -->",
"entryType": "function",
"jsdocTags": [],
"rawComment": "/**\n * When a view uses the {@link ChangeDetectionStrategy#OnPush} (checkOnce)\n * change detection strategy, explicitly marks the view as changed so that\n * it can be checked again.\n *\n * Components are normally marked as dirty (in need of rerendering) when inputs\n * have changed or events have fired in the view. Call this method to ensure that\n * a component is checked even if these triggers have not occurred.\n *\n * <!-- TODO: Add a link to a chapter on OnPush components -->\n *\n */"
},
"entryType": "function",
"description": "When a view uses the {@link ChangeDetectionStrategy#OnPush} (checkOnce)\nchange detection strategy, explicitly marks the view as changed so that\nit can be checked again.\n\nComponents are normally marked as dirty (in need of rerendering) when inputs\nhave changed or events have fired in the view. Call this method to ensure that\na component is checked even if these triggers have not occurred.\n\n<!-- TODO: Add a link to a chapter on OnPush components -->",
"jsdocTags": [],
"rawComment": "/**\n * When a view uses the {@link ChangeDetectionStrategy#OnPush} (checkOnce)\n * change detection strategy, explicitly marks the view as changed so that\n * it can be checked again.\n *\n * Components are normally marked as dirty (in need of rerendering) when inputs\n * have changed or events have fired in the view. Call this method to ensure that\n * a component is checked even if these triggers have not occurred.\n *\n * <!-- TODO: Add a link to a chapter on OnPush components -->\n *\n */",
"memberType": "method",
"memberTags": ["abstract", "override"]
},
{
"name": "detach",
"signatures": [
{
"name": "detach",
"entryType": "function",
"description": "Detaches this view from the change-detection tree.\nA detached view is not checked until it is reattached.\nUse in combination with `detectChanges()` to implement local change detection checks.\n\nDetached views are not checked during change detection runs until they are\nre-attached, even if they are marked as dirty.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n<!-- TODO: Add a live demo once ref.detectChanges is merged into master -->",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "/**\n * Detaches this view from the change-detection tree.\n * A detached view is not checked until it is reattached.\n * Use in combination with `detectChanges()` to implement local change detection checks.\n *\n * Detached views are not checked during change detection runs until they are\n * re-attached, even if they are marked as dirty.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->\n *\n */",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "detach",
"description": "Detaches this view from the change-detection tree.\nA detached view is not checked until it is reattached.\nUse in combination with `detectChanges()` to implement local change detection checks.\n\nDetached views are not checked during change detection runs until they are\nre-attached, even if they are marked as dirty.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n<!-- TODO: Add a live demo once ref.detectChanges is merged into master -->",
"entryType": "function",
"jsdocTags": [],
"rawComment": "/**\n * Detaches this view from the change-detection tree.\n * A detached view is not checked until it is reattached.\n * Use in combination with `detectChanges()` to implement local change detection checks.\n *\n * Detached views are not checked during change detection runs until they are\n * re-attached, even if they are marked as dirty.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->\n *\n */"
},
"entryType": "function",
"description": "Detaches this view from the change-detection tree.\nA detached view is not checked until it is reattached.\nUse in combination with `detectChanges()` to implement local change detection checks.\n\nDetached views are not checked during change detection runs until they are\nre-attached, even if they are marked as dirty.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n<!-- TODO: Add a live demo once ref.detectChanges is merged into master -->",
"jsdocTags": [],
"rawComment": "/**\n * Detaches this view from the change-detection tree.\n * A detached view is not checked until it is reattached.\n * Use in combination with `detectChanges()` to implement local change detection checks.\n *\n * Detached views are not checked during change detection runs until they are\n * re-attached, even if they are marked as dirty.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->\n *\n */",
"memberType": "method",
"memberTags": ["abstract", "override"]
},
{
"name": "detectChanges",
"signatures": [
{
"name": "detectChanges",
"entryType": "function",
"description": "Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach}\nto implement local change detection checks.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n<!-- TODO: Add a live demo once ref.detectChanges is merged into master -->",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "/**\n * Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach}\n * to implement local change detection checks.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->\n *\n */",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "detectChanges",
"description": "Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach}\nto implement local change detection checks.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n<!-- TODO: Add a live demo once ref.detectChanges is merged into master -->",
"entryType": "function",
"jsdocTags": [],
"rawComment": "/**\n * Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach}\n * to implement local change detection checks.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->\n *\n */"
},
"entryType": "function",
"description": "Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach}\nto implement local change detection checks.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n<!-- TODO: Add a live demo once ref.detectChanges is merged into master -->",
"jsdocTags": [],
"rawComment": "/**\n * Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach}\n * to implement local change detection checks.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->\n *\n */",
"memberType": "method",
"memberTags": ["abstract", "override"]
},
{
"name": "checkNoChanges",
"signatures": [
{
"name": "checkNoChanges",
"entryType": "function",
"description": "Checks the change detector and its children, and throws if any changes are detected.\n\nUse in development mode to verify that running change detection doesn't introduce\nother changes. Calling it in production mode is a noop.",
"generics": [],
"isNewType": false,
"jsdocTags": [
{
"name": "deprecated",
"comment": "This is a test-only API that does not have a place in production interface.\n`checkNoChanges` is already part of an `ApplicationRef` tick when the app is running in dev\nmode. For more granular `checkNoChanges` validation, use `ComponentFixture`."
}
],
"params": [],
"rawComment": "/**\n * Checks the change detector and its children, and throws if any changes are detected.\n *\n * Use in development mode to verify that running change detection doesn't introduce\n * other changes. Calling it in production mode is a noop.\n *\n * @deprecated This is a test-only API that does not have a place in production interface.\n * `checkNoChanges` is already part of an `ApplicationRef` tick when the app is running in dev\n * mode. For more granular `checkNoChanges` validation, use `ComponentFixture`.\n */",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "checkNoChanges",
"description": "Checks the change detector and its children, and throws if any changes are detected.\n\nUse in development mode to verify that running change detection doesn't introduce\nother changes. Calling it in production mode is a noop.",
"entryType": "function",
"jsdocTags": [
{
"name": "deprecated",
"comment": "This is a test-only API that does not have a place in production interface.\n`checkNoChanges` is already part of an `ApplicationRef` tick when the app is running in dev\nmode. For more granular `checkNoChanges` validation, use `ComponentFixture`."
}
],
"rawComment": "/**\n * Checks the change detector and its children, and throws if any changes are detected.\n *\n * Use in development mode to verify that running change detection doesn't introduce\n * other changes. Calling it in production mode is a noop.\n *\n * @deprecated This is a test-only API that does not have a place in production interface.\n * `checkNoChanges` is already part of an `ApplicationRef` tick when the app is running in dev\n * mode. For more granular `checkNoChanges` validation, use `ComponentFixture`.\n */"
},
"entryType": "function",
"description": "Checks the change detector and its children, and throws if any changes are detected.\n\nUse in development mode to verify that running change detection doesn't introduce\nother changes. Calling it in production mode is a noop.",
"jsdocTags": [
{
"name": "deprecated",
"comment": "This is a test-only API that does not have a place in production interface.\n`checkNoChanges` is already part of an `ApplicationRef` tick when the app is running in dev\nmode. For more granular `checkNoChanges` validation, use `ComponentFixture`."
}
],
"rawComment": "/**\n * Checks the change detector and its children, and throws if any changes are detected.\n *\n * Use in development mode to verify that running change detection doesn't introduce\n * other changes. Calling it in production mode is a noop.\n *\n * @deprecated This is a test-only API that does not have a place in production interface.\n * `checkNoChanges` is already part of an `ApplicationRef` tick when the app is running in dev\n * mode. For more granular `checkNoChanges` validation, use `ComponentFixture`.\n */",
"memberType": "method",
"memberTags": ["abstract", "override"]
},
{
"name": "reattach",
"signatures": [
{
"name": "reattach",
"entryType": "function",
"description": "Re-attaches the previously detached view to the change detection tree.\nViews are attached to the tree by default.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "/**\n * Re-attaches the previously detached view to the change detection tree.\n * Views are attached to the tree by default.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n *\n */",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "reattach",
"description": "Re-attaches the previously detached view to the change detection tree.\nViews are attached to the tree by default.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->",
"entryType": "function",
"jsdocTags": [],
"rawComment": "/**\n * Re-attaches the previously detached view to the change detection tree.\n * Views are attached to the tree by default.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n *\n */"
},
"entryType": "function",
"description": "Re-attaches the previously detached view to the change detection tree.\nViews are attached to the tree by default.\n\n<!-- TODO: Add a link to a chapter on detach/reattach/local digest -->",
"jsdocTags": [],
"rawComment": "/**\n * Re-attaches the previously detached view to the change detection tree.\n * Views are attached to the tree by default.\n *\n * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->\n *\n */",
"memberType": "method",
"memberTags": ["abstract", "override"]
}
],
"generics": [],
"description": "Represents an Angular view.",
"jsdocTags": [
{
"name": "see",
"comment": "[Change detection usage](/api/core/ChangeDetectorRef?tab=usage-notes)"
},
{"name": "publicApi", "comment": ""}
],
"rawComment": "/**\n * Represents an Angular view.\n *\n * @see [Change detection usage](/api/core/ChangeDetectorRef?tab=usage-notes)\n *\n * @publicApi\n */",
"source": {
"filePath": "/packages/core/src/linker/view_ref.ts",
"startLine": 18,
"endLine": 37
}
},
{
"name": "afterNextRender",
"signatures": [
{
"name": "afterNextRender",
"entryType": "function",
"description": "Register callbacks to be invoked the next time the application finishes rendering, during the\nspecified phases. The available phases are:\n- `earlyRead`\n Use this phase to **read** from the DOM before a subsequent `write` callback, for example to\n perform custom layout that the browser doesn't natively support. Prefer the `read` phase if\n reading can wait until after the write phase. **Never** write to the DOM in this phase.\n- `write`\n Use this phase to **write** to the DOM. **Never** read from the DOM in this phase.\n- `mixedReadWrite`\n Use this phase to read from and write to the DOM simultaneously. **Never** use this phase if\n it is possible to divide the work among the other phases instead.\n- `read`\n Use this phase to **read** from the DOM. **Never** write to the DOM in this phase.\n\n<div class=\"alert is-critical\">\n\nYou should prefer using the `read` and `write` phases over the `earlyRead` and `mixedReadWrite`\nphases when possible, to avoid performance degradation.\n\n</div>\n\nNote that:\n- Callbacks run in the following phase order *once, after the next render*:\n 1. `earlyRead`\n 2. `write`\n 3. `mixedReadWrite`\n 4. `read`\n- Callbacks in the same phase run in the order they are registered.\n- Callbacks run on browser platforms only, they will not run on the server.\n\nThe first phase callback to run as part of this spec will receive no parameters. Each\nsubsequent phase callback in this spec will receive the return value of the previously run\nphase callback as a parameter. This can be used to coordinate work across multiple phases.\n\nAngular is unable to verify or enforce that phases are used correctly, and instead\nrelies on each developer to follow the guidelines documented for each value and\ncarefully choose the appropriate one, refactoring their code if necessary. By doing\nso, Angular is better able to minimize the performance degradation associated with\nmanual DOM access, ensuring the best experience for the end users of your application\nor library.\n\n<div class=\"alert is-important\">\n\nComponents are not guaranteed to be [hydrated](guide/hydration) before the callback runs.\nYou must use caution when directly reading or writing the DOM and layout.\n\n</div>",
"generics": [
{"name": "E", "default": "never"},
{"name": "W", "default": "never"},
{"name": "M", "default": "never"}
],
"isNewType": false,
"jsdocTags": [
{"name": "param", "comment": "The callback functions to register"},
{"name": "param", "comment": "Options to control the behavior of the callback"},
{
"name": "usageNotes",
"comment": "Use `afterNextRender` to read or write the DOM once,\nfor example to initialize a non-Angular library.\n\n### Example\n```ts\n@Component({\n selector: 'my-chart-cmp',\n template: `<div #chart>{{ ... }}</div>`,\n})\nexport class MyChartCmp {\n @ViewChild('chart') chartRef: ElementRef;\n chart: MyChart|null;\n\n constructor() {\n afterNextRender({\n write: () => {\n this.chart = new MyChart(this.chartRef.nativeElement);\n }\n });\n }\n}\n```"
},
{"name": "developerPreview", "comment": ""}
],
"params": [
{
"name": "spec",
"description": "The callback functions to register",
"type": "{ earlyRead?: () => E; write?: (...args: [E] extends [never] ? [] : [E]) => W; mixedReadWrite?: (...args: [W] extends [never] ? [E] extends [never] ? [] : [E] : [W]) => M; read?: (...args: [...] extends [...] ? [...] extends [...] ? [...] extends [...] ? [] : [...] : [...] : [...]) => void; }",
"isOptional": false,
"isRestParam": false
},
{
"name": "options",
"description": "Options to control the behavior of the callback",
"type": "Omit<AfterRenderOptions, \"phase\">",
"isOptional": true,
"isRestParam": false
}
],
"rawComment": "/**\n * Register callbacks to be invoked the next time the application finishes rendering, during the\n * specified phases. The available phases are:\n * - `earlyRead`\n * Use this phase to **read** from the DOM before a subsequent `write` callback, for example to\n * perform custom layout that the browser doesn't natively support. Prefer the `read` phase if\n * reading can wait until after the write phase. **Never** write to the DOM in this phase.\n * - `write`\n * Use this phase to **write** to the DOM. **Never** read from the DOM in this phase.\n * - `mixedReadWrite`\n * Use this phase to read from and write to the DOM simultaneously. **Never** use this phase if\n * it is possible to divide the work among the other phases instead.\n * - `read`\n * Use this phase to **read** from the DOM. **Never** write to the DOM in this phase.\n *\n * <div class=\"alert is-critical\">\n *\n * You should prefer using the `read` and `write` phases over the `earlyRead` and `mixedReadWrite`\n * phases when possible, to avoid performance degradation.\n *\n * </div>\n *\n * Note that:\n * - Callbacks run in the following phase order *once, after the next render*:\n * 1. `earlyRead`\n * 2. `write`\n * 3. `mixedReadWrite`\n * 4. `read`\n * - Callbacks in the same phase run in the order they are registered.\n * - Callbacks run on browser platforms only, they will not run on the server.\n *\n * The first phase callback to run as part of this spec will receive no parameters. Each\n * subsequent phase callback in this spec will receive the return value of the previously run\n * phase callback as a parameter. This can be used to coordinate work across multiple phases.\n *\n * Angular is unable to verify or enforce that phases are used correctly, and instead\n * relies on each developer to follow the guidelines documented for each value and\n * carefully choose the appropriate one, refactoring their code if necessary. By doing\n * so, Angular is better able to minimize the performance degradation associated with\n * manual DOM access, ensuring the best experience for the end users of your application\n * or library.\n *\n * <div class=\"alert is-important\">\n *\n * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.\n * You must use caution when directly reading or writing the DOM and layout.\n *\n * </div>\n *\n * @param spec The callback functions to register\n * @param options Options to control the behavior of the callback\n *\n * @usageNotes\n *\n * Use `afterNextRender` to read or write the DOM once,\n * for example to initialize a non-Angular library.\n *\n * ### Example\n * ```ts\n * @Component({\n * selector: 'my-chart-cmp',\n * template: `<div #chart>{{ ... }}</div>`,\n * })\n * export class MyChartCmp {\n * @ViewChild('chart') chartRef: ElementRef;\n * chart: MyChart|null;\n *\n * constructor() {\n * afterNextRender({\n * write: () => {\n * this.chart = new MyChart(this.chartRef.nativeElement);\n * }\n * });\n * }\n * }\n * ```\n *\n * @developerPreview\n */",
"returnType": "AfterRenderRef"
},
{
"name": "afterNextRender",
"entryType": "function",
"description": "Register a callback to be invoked the next time the application finishes rendering, during the\n`mixedReadWrite` phase.\n\n<div class=\"alert is-critical\">\n\nYou should prefer specifying an explicit phase for the callback instead, or you risk significant\nperformance degradation.\n\n</div>\n\nNote that the callback will run\n- in the order it was registered\n- on browser platforms only\n- during the `mixedReadWrite` phase\n\n<div class=\"alert is-important\">\n\nComponents are not guaranteed to be [hydrated](guide/hydration) before the callback runs.\nYou must use caution when directly reading or writing the DOM and layout.\n\n</div>",
"generics": [],
"isNewType": false,
"jsdocTags": [
{"name": "param", "comment": "A callback function to register"},
{"name": "param", "comment": "Options to control the behavior of the callback"},
{
"name": "usageNotes",
"comment": "Use `afterNextRender` to read or write the DOM once,\nfor example to initialize a non-Angular library.\n\n### Example\n```ts\n@Component({\n selector: 'my-chart-cmp',\n template: `<div #chart>{{ ... }}</div>`,\n})\nexport class MyChartCmp {\n @ViewChild('chart') chartRef: ElementRef;\n chart: MyChart|null;\n\n constructor() {\n afterNextRender({\n write: () => {\n this.chart = new MyChart(this.chartRef.nativeElement);\n }\n });\n }\n}\n```"
},
{"name": "developerPreview", "comment": ""}
],
"params": [
{
"name": "callback",
"description": "A callback function to register",
"type": "VoidFunction",
"isOptional": false,
"isRestParam": false
},
{
"name": "options",
"description": "Options to control the behavior of the callback",
"type": "AfterRenderOptions",
"isOptional": true,
"isRestParam": false
}
],
"rawComment": "/**\n * Register a callback to be invoked the next time the application finishes rendering, during the\n * `mixedReadWrite` phase.\n *\n * <div class=\"alert is-critical\">\n *\n * You should prefer specifying an explicit phase for the callback instead, or you risk significant\n * performance degradation.\n *\n * </div>\n *\n * Note that the callback will run\n * - in the order it was registered\n * - on browser platforms only\n * - during the `mixedReadWrite` phase\n *\n * <div class=\"alert is-important\">\n *\n * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.\n * You must use caution when directly reading or writing the DOM and layout.\n *\n * </div>\n *\n * @param callback A callback function to register\n * @param options Options to control the behavior of the callback\n *\n * @usageNotes\n *\n * Use `afterNextRender` to read or write the DOM once,\n * for example to initialize a non-Angular library.\n *\n * ### Example\n * ```ts\n * @Component({\n * selector: 'my-chart-cmp',\n * template: `<div #chart>{{ ... }}</div>`,\n * })\n * export class MyChartCmp {\n * @ViewChild('chart') chartRef: ElementRef;\n * chart: MyChart|null;\n *\n * constructor() {\n * afterNextRender({\n * write: () => {\n * this.chart = new MyChart(this.chartRef.nativeElement);\n * }\n * });\n * }\n * }\n * ```\n *\n * @developerPreview\n */",
"returnType": "AfterRenderRef"
}
],
"implementation": {
"params": [
{
"name": "callbackOrSpec",
"description": "",
"type": "VoidFunction | { earlyRead?: () => unknown; write?: (r?: unknown) => unknown; mixedReadWrite?: (r?: unknown) => unknown; read?: (r?: unknown) => void; }",
"isOptional": false,
"isRestParam": false
},
{
"name": "options",
"description": "",
"type": "AfterRenderOptions",
"isOptional": true,
"isRestParam": false
}
],
"isNewType": false,
"returnType": "AfterRenderRef",
"generics": [],
"name": "afterNextRender",
"description": "",
"entryType": "function",
"jsdocTags": [],
"rawComment": ""
},
"entryType": "function",
"description": "",
"jsdocTags": [],
"rawComment": "",
"source": {
"fileName": "/packages/core/src/user-profile.ts",
"line": 3,
"character": 7
"filePath": "/packages/core/src/render3/after_render_hooks.ts",
"startLine": 446,
"endLine": 454
}
},
{
"name": "provideClientHydration",
"signatures": [
{
"name": "provideClientHydration",
"entryType": "function",
"description": "Sets up providers necessary to enable hydration functionality for the application.\n\nBy default, the function enables the recommended set of features for the optimal\nperformance for most of the applications. It includes the following features:\n\n* Reconciling DOM hydration. Learn more about it [here](guide/hydration).\n* [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and\ntransferring this cache to the client to avoid extra HTTP requests. Learn more about data caching\n[here](guide/ssr#caching-data-when-using-httpclient).\n\nThese functions allow you to disable some of the default features or configure features\n* {@link withNoHttpTransferCache} to disable HTTP transfer cache\n* {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options",
"generics": [],
"isNewType": false,
"jsdocTags": [
{
"name": "usageNotes",
"comment": "Basic example of how you can enable hydration in your application when\n`bootstrapApplication` function is used:\n```\nbootstrapApplication(AppComponent, {\n providers: [provideClientHydration()]\n});\n```\n\nAlternatively if you are using NgModules, you would add `provideClientHydration`\nto your root app module's provider list.\n```\n@NgModule({\n declarations: [RootCmp],\n bootstrap: [RootCmp],\n providers: [provideClientHydration()],\n})\nexport class AppModule {}\n```"
},
{"name": "see", "comment": "{@link withNoHttpTransferCache}"},
{"name": "see", "comment": "{@link withHttpTransferCacheOptions}"},
{
"name": "param",
"comment": "Optional features to configure additional router behaviors."
},
{"name": "returns", "comment": "A set of providers to enable hydration."},
{"name": "publicApi", "comment": ""}
],
"params": [
{
"name": "features",
"description": "Optional features to configure additional router behaviors.",
"type": "HydrationFeature<HydrationFeatureKind>[]",
"isOptional": false,
"isRestParam": true
}
],
"rawComment": "/**\n * Sets up providers necessary to enable hydration functionality for the application.\n *\n * By default, the function enables the recommended set of features for the optimal\n * performance for most of the applications. It includes the following features:\n *\n * * Reconciling DOM hydration. Learn more about it [here](guide/hydration).\n * * [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and\n * transferring this cache to the client to avoid extra HTTP requests. Learn more about data caching\n * [here](guide/ssr#caching-data-when-using-httpclient).\n *\n * These functions allow you to disable some of the default features or configure features\n * * {@link withNoHttpTransferCache} to disable HTTP transfer cache\n * * {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options\n *\n * @usageNotes\n *\n * Basic example of how you can enable hydration in your application when\n * `bootstrapApplication` function is used:\n * ```\n * bootstrapApplication(AppComponent, {\n * providers: [provideClientHydration()]\n * });\n * ```\n *\n * Alternatively if you are using NgModules, you would add `provideClientHydration`\n * to your root app module's provider list.\n * ```\n * @NgModule({\n * declarations: [RootCmp],\n * bootstrap: [RootCmp],\n * providers: [provideClientHydration()],\n * })\n * export class AppModule {}\n * ```\n *\n * @see {@link withNoHttpTransferCache}\n * @see {@link withHttpTransferCacheOptions}\n *\n * @param features Optional features to configure additional router behaviors.\n * @returns A set of providers to enable hydration.\n *\n * @publicApi\n */",
"returnType": "EnvironmentProviders"
}
],
"implementation": {
"params": [
{
"name": "features",
"description": "Optional features to configure additional router behaviors.",
"type": "HydrationFeature<HydrationFeatureKind>[]",
"isOptional": false,
"isRestParam": true
}
],
"isNewType": false,
"returnType": "EnvironmentProviders",
"returnDescription": "A set of providers to enable hydration.",
"generics": [],
"name": "provideClientHydration",
"description": "Sets up providers necessary to enable hydration functionality for the application.\n\nBy default, the function enables the recommended set of features for the optimal\nperformance for most of the applications. It includes the following features:\n\n* Reconciling DOM hydration. Learn more about it [here](guide/hydration).\n* [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and\ntransferring this cache to the client to avoid extra HTTP requests. Learn more about data caching\n[here](guide/ssr#caching-data-when-using-httpclient).\n\nThese functions allow you to disable some of the default features or configure features\n* {@link withNoHttpTransferCache} to disable HTTP transfer cache\n* {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options",
"entryType": "function",
"jsdocTags": [
{
"name": "usageNotes",
"comment": "Basic example of how you can enable hydration in your application when\n`bootstrapApplication` function is used:\n```\nbootstrapApplication(AppComponent, {\n providers: [provideClientHydration()]\n});\n```\n\nAlternatively if you are using NgModules, you would add `provideClientHydration`\nto your root app module's provider list.\n```\n@NgModule({\n declarations: [RootCmp],\n bootstrap: [RootCmp],\n providers: [provideClientHydration()],\n})\nexport class AppModule {}\n```"
},
{"name": "see", "comment": "{@link withNoHttpTransferCache}"},
{"name": "see", "comment": "{@link withHttpTransferCacheOptions}"},
{
"name": "param",
"comment": "Optional features to configure additional router behaviors."
},
{"name": "returns", "comment": "A set of providers to enable hydration."},
{"name": "publicApi", "comment": ""}
],
"rawComment": "/**\n * Sets up providers necessary to enable hydration functionality for the application.\n *\n * By default, the function enables the recommended set of features for the optimal\n * performance for most of the applications. It includes the following features:\n *\n * * Reconciling DOM hydration. Learn more about it [here](guide/hydration).\n * * [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and\n * transferring this cache to the client to avoid extra HTTP requests. Learn more about data caching\n * [here](guide/ssr#caching-data-when-using-httpclient).\n *\n * These functions allow you to disable some of the default features or configure features\n * * {@link withNoHttpTransferCache} to disable HTTP transfer cache\n * * {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options\n *\n * @usageNotes\n *\n * Basic example of how you can enable hydration in your application when\n * `bootstrapApplication` function is used:\n * ```\n * bootstrapApplication(AppComponent, {\n * providers: [provideClientHydration()]\n * });\n * ```\n *\n * Alternatively if you are using NgModules, you would add `provideClientHydration`\n * to your root app module's provider list.\n * ```\n * @NgModule({\n * declarations: [RootCmp],\n * bootstrap: [RootCmp],\n * providers: [provideClientHydration()],\n * })\n * export class AppModule {}\n * ```\n *\n * @see {@link withNoHttpTransferCache}\n * @see {@link withHttpTransferCacheOptions}\n *\n * @param features Optional features to configure additional router behaviors.\n * @returns A set of providers to enable hydration.\n *\n * @publicApi\n */"
},
"entryType": "function",
"description": "Sets up providers necessary to enable hydration functionality for the application.\n\nBy default, the function enables the recommended set of features for the optimal\nperformance for most of the applications. It includes the following features:\n\n* Reconciling DOM hydration. Learn more about it [here](guide/hydration).\n* [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and\ntransferring this cache to the client to avoid extra HTTP requests. Learn more about data caching\n[here](guide/ssr#caching-data-when-using-httpclient).\n\nThese functions allow you to disable some of the default features or configure features\n* {@link withNoHttpTransferCache} to disable HTTP transfer cache\n* {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options",
"jsdocTags": [
{
"name": "usageNotes",
"comment": "Basic example of how you can enable hydration in your application when\n`bootstrapApplication` function is used:\n```\nbootstrapApplication(AppComponent, {\n providers: [provideClientHydration()]\n});\n```\n\nAlternatively if you are using NgModules, you would add `provideClientHydration`\nto your root app module's provider list.\n```\n@NgModule({\n declarations: [RootCmp],\n bootstrap: [RootCmp],\n providers: [provideClientHydration()],\n})\nexport class AppModule {}\n```"
},
{"name": "see", "comment": "{@link withNoHttpTransferCache}"},
{"name": "see", "comment": "{@link withHttpTransferCacheOptions}"},
{"name": "param", "comment": "Optional features to configure additional router behaviors."},
{"name": "returns", "comment": "A set of providers to enable hydration."},
{"name": "publicApi", "comment": ""}
],
"rawComment": "/**\n * Sets up providers necessary to enable hydration functionality for the application.\n *\n * By default, the function enables the recommended set of features for the optimal\n * performance for most of the applications. It includes the following features:\n *\n * * Reconciling DOM hydration. Learn more about it [here](guide/hydration).\n * * [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and\n * transferring this cache to the client to avoid extra HTTP requests. Learn more about data caching\n * [here](guide/ssr#caching-data-when-using-httpclient).\n *\n * These functions allow you to disable some of the default features or configure features\n * * {@link withNoHttpTransferCache} to disable HTTP transfer cache\n * * {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options\n *\n * @usageNotes\n *\n * Basic example of how you can enable hydration in your application when\n * `bootstrapApplication` function is used:\n * ```\n * bootstrapApplication(AppComponent, {\n * providers: [provideClientHydration()]\n * });\n * ```\n *\n * Alternatively if you are using NgModules, you would add `provideClientHydration`\n * to your root app module's provider list.\n * ```\n * @NgModule({\n * declarations: [RootCmp],\n * bootstrap: [RootCmp],\n * providers: [provideClientHydration()],\n * })\n * export class AppModule {}\n * ```\n *\n * @see {@link withNoHttpTransferCache}\n * @see {@link withHttpTransferCacheOptions}\n *\n * @param features Optional features to configure additional router behaviors.\n * @returns A set of providers to enable hydration.\n *\n * @publicApi\n */",
"source": {
"filePath": "/packages/platform-browser/src/hydration.ts",
"startLine": 196,
"endLine": 233
}
}
]

View file

@ -0,0 +1,52 @@
import {runfiles} from '@bazel/runfiles';
import {readFile} from 'fs/promises';
import {JSDOM} from 'jsdom';
import {renderEntry} from '../rendering';
import {getRenderable} from '../processing';
import {initHighlighter} from '../shiki/shiki';
import {configureMarkedGlobally} from '../marked/configuration';
describe('markdown to html', () => {
const entries = new Map<string, DocumentFragment>();
const entries2 = new Map<string, string>();
beforeAll(async () => {
await initHighlighter();
await configureMarkedGlobally();
const entryContent = await readFile(runfiles.resolvePackageRelative('fake-entries.json'), {
encoding: 'utf-8',
});
const entryJson = JSON.parse(entryContent) as any;
for (const entry of entryJson.entries) {
const renderableJson = getRenderable(entry, '@angular/fakeentry');
const fragment = JSDOM.fragment(await renderEntry(renderableJson));
entries.set(entry['name'], fragment);
entries2.set(entry['name'], await renderEntry(renderableJson));
}
});
it('should render description correctly', () => {
const afterNextRenderEntry = entries.get('afterNextRender')!;
const header = afterNextRenderEntry.querySelector('.docs-reference-header')!;
expect(header).toBeDefined();
expect(header.outerHTML).not.toContain('```');
console.log(entries2.get('afterNextRender'));
const list = afterNextRenderEntry.querySelector('ul')!;
expect(list).toBeDefined();
// List are rendered
expect(list.outerHTML).toContain('<li>');
// Code blocks are rendered
expect(list.outerHTML).toContain('<code>mixedReadWrite</code>');
});
it('should render multiple {@link} blocks', () => {
const provideClientHydrationEntry = entries.get('provideClientHydration')!;
expect(provideClientHydrationEntry).toBeDefined();
const cardItem = provideClientHydrationEntry.querySelector('.docs-reference-card-item ')!;
expect(cardItem.innerHTML).not.toContain('@link');
});
});

View file

@ -37,6 +37,7 @@ export const JS_DOC_DESCRIPTION_TAG = 'description';
// Some links are written in the following format: {@link Route}
const jsDoclinkRegex = /\{\s*@link\s+([^}]+)\s*\}/;
const jsDoclinkRegexGlobal = /\{\s*@link\s+([^}]+)\s*\}/g;
/** Given an entity with a description, gets the entity augmented with an `htmlDescription`. */
export function addHtmlDescription<T extends HasDescription & HasModuleName>(
@ -196,7 +197,7 @@ function convertJsDocExampleToHtmlExample(text: string): string {
}
function convertLinks(text: string, entry: HasModuleName) {
return text.replace(jsDoclinkRegex, (_, link) => {
return text.replace(jsDoclinkRegexGlobal, (_, link) => {
const [symbol, description] = link.split(/\s(.+)/);
if (symbol && description) {
// {@link Route Some route with description}

View file

@ -7,4 +7,6 @@
- Order
- here
- matter
- doesn't
- doesn't
- [some link](https://angular.dev)
- Code block `SomeClass`

View file

@ -21,7 +21,16 @@ describe('markdown to html', () => {
const unorderedList = markdownDocument.querySelector('ul');
expect(unorderedList?.className).toBe('docs-list');
expect(unorderedList?.childElementCount).toBe(4);
expect(unorderedList?.childElementCount).toBe(6);
expect(unorderedList?.textContent).toContain('matter');
});
it('should render list items', () => {
const unorderedList = markdownDocument.querySelector('ul');
const linkItem = unorderedList!.children[4];
expect(linkItem.outerHTML).toContain('href="https://angular.dev"');
const codeItem = unorderedList!.children[5];
expect(codeItem.outerHTML).toContain('<code>SomeClass</code>');
});
});