Compare commits

..

161 Commits

Author SHA1 Message Date
Miles Malerba 4039f073f8 Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-08-26 15:14:08 -07:00
Miles Malerba 7f19f7b825 refactor(forms): rename private access utilities (#63407) 2025-08-26 14:38:59 -07:00
Miles Malerba b23c31a496 fix(forms): fix some things that g3 complains about (#63405)
* fix(forms): fix some things that g3 complains about

* fixup! fix(forms): fix some things that g3 complains about
2025-08-26 12:09:56 -07:00
Miles Malerba aef4b05c62 build: cleanup accidental changes from merging main 2025-08-25 18:53:25 -07:00
Miles Malerba 52c7bde082 build: fix outdated build rule references 2025-08-25 17:13:20 -07:00
Miles Malerba 71557463f0 Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-08-25 17:09:02 -07:00
Miles Malerba 447b6e958f refactor(forms): move addDefaultField out of public API (#63389) 2025-08-25 17:05:25 -07:00
Miles Malerba f90ca1cfcf Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-08-25 15:17:37 -07:00
Miles Malerba d416beee7b fix(form): set select value after option renders (#63340) 2025-08-25 09:24:29 -07:00
Miles Malerba 0b00039a45 fix(forms): ensure pending status propagates to parent (#63333)
* fix(forms): ensure pending status propagates to parent

* test(forms): add some additional tests for pending status

* refactor(forms): better variable naming

use more descriptive names for variables

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

---------

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-08-22 11:09:13 -07:00
Miles Malerba 4065c5f715 build(forms): restore api golden generation (#63336) 2025-08-22 11:07:39 -07:00
Miles Malerba 37661b6ec1 Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-08-21 17:18:20 -07:00
Miles Malerba 170a4376ea docs(forms): address feedback on #63293 (#63322)
* docs(forms): address feedback on #63293

* another round of feedback addressed
2025-08-21 17:12:44 -07:00
Leon Senft 197b393aa6 perf(forms): enable tree shaking of unused errors and properties
The `ValidationError` and `AggregateProperty` classes used static
factory methods to create instances. This pattern created a hard
dependency from the base classes to all of their subclasses and
variations.

As a result, any application importing even a single validator (e.g.,
required) or property would inadvertently pull in the code for all other
validators and properties, preventing effective tree-shaking and
increasing bundle size.

This refactoring removes the static factory methods and replaces them
with standalone, exported functions or properties. This decouples the
individual validators and properties, allowing bundlers to properly
perform dead code elimination.
2025-08-21 16:58:59 -07:00
Miles Malerba 2bbd1c972d feat(forms): allow setting custom error message (#63314)
* feat(forms): allow setting custom error message

allows standard validator logic to specify custom error message

* address feedback

* address feedback
2025-08-21 14:54:57 -07:00
Miles Malerba 6434bcce0c fix(forms): minor cleanup for disabled API (#63318)
* fix(forms): minor cleanup for disabled API

rename DisabledReason.reason to DisabledReason.message for consistency with errors, and allow unconditional disabling with message

* address feedback
2025-08-21 14:48:40 -07:00
Miles Malerba aa51dcdcdc feat(forms): add support for binding to select (#63300)
Adds test for select, and textarea (which was untested)
2025-08-20 14:14:06 -07:00
Miles Malerba 46e3de0f0a cleanup unrelated formatting changes on prototype branch 2025-08-20 10:14:44 -07:00
Miles Malerba dc966ceb6a Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-08-20 10:06:52 -07:00
Leon Senft c303673a65 refactor(forms): relocate the signal based forms implementation
Move the signal based forms implementation from
`@angular/forms/experimental` to `@angular/forms/signals` in preparation
of merging into `main`.
2025-08-20 08:15:02 -07:00
Miles Malerba 9866f91ef6 Review pass & fixup of the control directive & related code (#63068)
* feat(forms): add missing builtin property hooks for custom controls

* docs(forms): address some todos

* feat(forms): add missing form control inputs & tsdoc

* fix(forms): correct some properties on InteropNgControl

Also notes a few properties in comments that we should consider supporting if possible

* docs(forms): tsdoc and minor fixup for controls/control.ts

* test: move control directive test to web test

this allows cleaning up hacky test code to run it in node

* fixup! test: move control directive test to web test

* fixup! feat(forms): add missing form control inputs & tsdoc

* fixup! docs(forms): tsdoc and minor fixup for controls/control.ts

* fixup! fixup! feat(forms): add missing form control inputs & tsdoc

* address review comments

* fix comment

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

* test: cleanup test

* remove extraneous undefined

* resolve some TODOs

* test: move to new inputBinding syntax

* test: update goldens

* fix test

---------

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-08-19 16:09:05 -07:00
Leon Senft b86b1e8810 feat(forms): add error summaries to fields
Add an `errorSummary` signal to `FieldState` which contains all of the
validation errors of a field and its descendants. This can be used to
conveniently collect all form validation errors by reading it from the
root field.
2025-08-19 14:16:04 -07:00
Leon Senft 768f0410b9 feat(forms): add associated field to all validation errors
Previously only tree validation errors carried their associated fields.
Regular validation errors didn't carry their field because it was
assumed it could be trivially inferred from context.

Error summaries (which are forthcoming) break this assumption, as they
combine both regular and tree validation errors from an entire form into
a single list. Without explicitly carrying their associated field,
there's no way of knowing to which field a regular validation error is
associated when obtained through an error summary.

Note that while all validation errors now have a field, it's not
explicitly required to define one when returning an error from a logic
function callback in `validateTree()`, nor is it allowed in
`validate()`. These functions will automatically add the current field
by default to any errors missing a field definition. This ensures that
all validation errors returned from fields (e.g. `field.path.errors()`)
_do_ have a defined field.

This change makes testing errors by equality more tedious; however, we
only do so in our own tests to be comprehensive. In reality, it's
expected that developers would simply check for an expected `error.kind`
or similar property of a custom error.
2025-08-19 13:01:42 -07:00
Miles Malerba 9a0c96489a fix(forms): align FieldPath type with implementation (#63100)
* test(forms): rename outdated test file name

* split recursive logic tests into a separate file

* add tests to demonstrate issue

* fix(forms): align FieldPath type with implementation

In particular when dealing with model values that are potentially
undefined, the typing did not extract the `undefined` outside. With this
fix the `undefined` is extracted, preventing people form defining logic
on fields that might not actually exist.

* fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* address feedback

* ci: update goldens

* type cleanup

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-08-18 17:06:26 -07:00
Leon Senft 1545ddcb4b refactor(forms): reduce FieldState API to a minimal set of properties
Remove the following properties from `FieldState`

* `syncErrors`
* `syncValid`

These properties are implementation details which can be accessed
internally through the field node's `validationState` where necessary.
2025-08-18 16:02:12 -07:00
Leon Senft fdbb09f2ec refactor(forms): fix typo in class name
Rename `NonMergableLogicNodeBuilder` to `NonMergeableLogicNodeBuilder`.
2025-08-18 09:33:59 -07:00
Kirill Cherkashin 04f225d516 Add forms adapter (#62940)
* refactor(forms): Add adapter support

Adapter allowing customization of the creation logic for a field and its associated structure and state.
2025-08-11 15:29:30 +02:00
Miles Malerba 2d03187a1d feat(forms): track bound controls as field state (#63060)
* feat(forms): track bound controls as field state

Adds a signal to the field state that tracks the instances of `Control` that are currently bound to the field. This is useful for error summaries that want to support jumping to fields with errors

* docs(forms): fix typo

fix typo blah blah this commit message is long enough

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-08-09 10:25:52 +09:00
Miles Malerba cb826866f3 refactor(forms): reorganize control directive & add disabled/readonly (#63058)
* refactor(forms): reorganize control directive & add disabled/readonly

* fix(forms): control directive propagates metadata

* feat(forms): support FieldState.name() & propagate to controls

* feat(forms): improve custom control support

Split `FormUiControl` into `FormValueControl` and `FormCheckboxControl`,
which capture the two main types of UI control:

 * those that expose a typed `.value()`
 * those that expose a boolean `.checked()`

Implement both in the `Control` directive.

* fix(forms): set required inputs with somewhat correct timing

If `[control]` is applied to a custom UI control, it wants to synchronize
state in the field w/ the inputs of that custom control. This is difficult
to do in user-land. We use `effect`, but effects don't run before the
lifecycle hooks of the component. This is usually okay, but has one
significant issue: the UI control's required inputs won't be set in time for
those lifecycle hooks to run.

Eventually we can build custom functionality for the `Control` directive
into the framework, but for now we work around this limitation with a hack.
We use an `@Input` instead of a signal-based `input()` for the `[control]`
to hook the exact moment inputs are being set, before the important
lifecycle hooks of the UI control. We can then initialize all our effects
and force them to run immediately, ensuring all required inputs have values.

* feat(forms): make `Control` a no-op for components with a control input

When a component is used which represents a subform, it can use a `control`
input to receive and pass down a `Field` to child controls. In that case,
the `Control` directive will match against the component, but should do
nothing since the component is already handling the `Field` propagation
internally.

* test(forms): switch tests to zoneless

Switch the test configuration to avoid pulling in zone.js, purely to avoid
the console warning spam.

* refactor(forms): address review feedback

address feedback on #62930 and  #62933

* docs(forms): add TODOs for open questions

adds TODOs for open questions on the original PRs, they can be settled in a followup

closes #62930
closes #62933

* build(forms): update api goldens

---------

Co-authored-by: Alex Rickabaugh <alx+alxhub@alxandria.net>
2025-08-08 10:41:25 +09:00
Miles Malerba e127c420f6 feat(forms): switch to RegExp in pattern validator (#63045)
* feat(forms): switch to RegExp in pattern validator

No other libraries use string patterns for validation

* refactor(forms): update PATTERN to refer to a RegExp

addresses feedback from the original PR

* docs(forms): update docs for pattern logic function

aligns the tsdoc with other validatior logic functions

* build: update api goldens

updates the api goldens to reflect changes in this PR

---------

Co-authored-by: Kirill Cherkashin <kirjs@google.com>
2025-08-08 09:18:15 +09:00
Leon Senft 21838501b6 feat(forms): prevent invalid form submission
Return from `submit()` before running the `action` callback if the form
is invalid. Note that `submit()` will still mark all fields as touched
before returning to ensure that all validation errors are displayed.
2025-08-05 13:55:24 -07:00
Leon Senft 6dbe4b7369 fix(forms): memoize deepSignal value
Test that submission errors are cleared from their corresponding field
when its value is changed. Submission errors should not be cleared from
fields whose values remain unchanged. This behavior relies on fields
memoizing their value, which `deepSignal()` now does.
2025-08-05 13:48:26 -07:00
Leon Senft cd8e0c3ae0 Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-08-05 11:53:19 -07:00
Miles Malerba 615f85f84e refactor(forms): reorganize and improve tsdoc for the schema (#62915)
* refactor(forms): move schema impl under a schema directory

* refactor(forms): separate out logic.ts and logic_node.ts

* docs: improve tsdoc for schema.ts

* docs: improve tsdoc for path_node.ts

* docs: improve tsdoc for logic.ts and logic_node.ts

* docs: fix a gazillion typos

* fixup! docs: fix a gazillion typos

* fixup! docs: fix a gazillion typos

* fixup! refactor(forms): separate out logic.ts and logic_node.ts

* fixup! refactor(forms): separate out logic.ts and logic_node.ts

* fixup! docs: improve tsdoc for path_node.ts

* fixup! docs: improve tsdoc for path_node.ts

* fixup! refactor(forms): separate out logic.ts and logic_node.ts

* fixup! 6e425b880a
2025-08-03 20:47:03 +09:00
Miles Malerba 0c82de5fe5 refactor(forms): cleanup util (#62911)
Move illegal.ts into util and consolidate multiple implementations of
isObejct
2025-08-01 16:08:54 +09:00
Miles Malerba 9349bcfee7 fix(forms): fix triage logic for server errors and improve field tsdoc (#62910)
* docs(forms): Improve tsdoc for field structure

Adds / improves tsdoc for field/structure.ts

* fix(forms): fix server error triage logic

fixes a bug where server errors could be lost if targeting multiple of
them at the same field

* docs: fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* docs: fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* docs: fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* docs: address feedback

* refactor(forms): address ai review

address comments from gemini bot

* refactor(forms): clean up set server errors

Clean up the set server errors logic so it doesn't allocate unnecessary data structures.

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

* fixup! docs(forms): Improve tsdoc for field structure

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-08-01 15:25:11 +09:00
Kirill Cherkashin 3a4caec61c fix(forms): expose and test hidden (#62905)
This would allow using hidden

Co-authored-by: kirjs <kirjs@google.com>
2025-07-31 12:11:13 -04:00
Miles Malerba b24fec7215 docs and cleanup for field implementation (#62888)
* refactor(forms): improve docs and typing for field

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

* fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

---------

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-31 11:04:16 +09:00
Leon Senft 5747c7a48b refactor(forms): remove unsubmitted and submitted field states
* `FieldState.submittedStatus` is replaced by `FieldState.submitting`,
  which tracks only whether the form is currently being submitted. This
  state is propagated downwards to all descendants of the submitted field.

* `submit()` now marks the field and all of its descendants as touched
  to ensure that any validation errors are displayed upon submission
  attempts.
2025-07-30 11:18:13 -07:00
Leon Senft cf2be036c9 test(forms): update failing API golden test
Fix the last remaining CI failure on the prototype/signal-forms branch
for now.
2025-07-30 10:21:50 -07:00
Leon Senft b93a1b8df7 Fix CI (#62887)
* fix(forms): fix `yarn ts-circular-deps:check` errors

Remove circular dependencies.

* Use `import type` where possible instead of `import`.
* Split the contents of `resolution.ts` out from `context.ts`.
* Import symbols directly from their defining files rather than through
  `public_api.ts`.

* fix(forms): fix failing test case for async validation using a standard schema

This test case relied on Zone.js converting `async () => true` to a
`Promise`, which was necessary to make validation run asynchronously.
After merging the `main` branch, this test started failing because the
validator completed synchronously despite the use of an `async`
function. Using `Promise.resolve()` guarantees that the validation will
run asynchronously and fixes the failure.
2025-07-30 23:29:03 +09:00
Leon Senft 6dd4f7e67a fix(signal-forms): fix tslint errors
Add explicit types to fix `no-exported-inferred-call-type` violations.
2025-07-29 13:37:23 -07:00
Leon Senft e6e0c72118 Merge remote-tracking branch 'upstream/main' into prototype/signal-forms 2025-07-29 13:21:25 -07:00
Miles Malerba 32c9aadd6a docs/test for async validation running only after sync valid (#62830)
* add test for validateHttp on root path

* docs: calrify that async validation only runs when sync valid

* address ai review
2025-07-29 09:04:03 +09:00
Miles Malerba 862e7708e3 cleanup & type improvements for standard schema validator (#62845)
* move standard schema validator under validators

* make zod a dev dep and update to v4

* fixes and tests for validateStandardSchema typing

* switch node tests to zoneless and remove web tests

This resolves the issue with our node tests that forced us to split the
Zod tests out into a separate web test. With this commit the node tests
are fixed and the Zod-based tests are moved back under node.

* minor docs fixup for validation errors

* support deep instances of z.looseObject

* address ai review

* address feedback
2025-07-29 09:03:02 +09:00
Miles Malerba 88614f0a1b Code cleanup and docs for some of the field node implementation (#62847)
* cleanup for field/context

* cleanup for field/util

* cleanup for field/manager

* cleanup for field/node

* cleanup for field/property

* update wording

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

* fix typo

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

---------

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-07-29 08:33:15 +09:00
Miles Malerba 21ca05d5c9 refactor(signal-forms): cleanup for api/logic & api/structure (#62810)
* remove leftover todo

* docs pass for api/logic

* remove false from ValidationResult

In practice I found the ability to return false quite confusing.
It meant you could do `x && ValidationError.custom()`, but not
`x || ValidationError.custom()`. Making both of those work would mean
that returning `true` from `validate` vs returning `false` would mean
the exact same thing (no errors). That's also super odd, so I opted to
just remove `false` as a possibility.

* cleanup/docs pass for api/structure

* fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* address feedback

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-25 13:22:34 -07:00
Leon Senft e30ff9f688 refactor(signal-forms): add dependency on @standard-schema/spec 2025-07-24 16:07:26 -07:00
Miles Malerba f1105f9bab refactor(signal-forms): some cleanup for deepSignal & types (#62753)
* cleanup/docs pass for deepSignal

* cleanup/docs for api/types

* refactor the tree & async validation result types

These types have been made more accurate, to reflect the fact that a
tree or async validator could return a mix of errors with and without
fields.

Also added our own copy of isArray since the built-in one doesn't handle
readonly arrays properly.

* fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* address comments

* fix typo

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-07-24 14:06:33 -07:00
Leon Senft b0c962db4a refactor(signal-forms): remove unnecessary no-op schema
Use the function overload for `form()` without a schema.
2025-07-23 11:56:38 -07:00
Leon Senft c1085fc134 fixup! feat(signal-forms): add support for resetting forms 2025-07-23 11:56:38 -07:00
Leon Senft 124fab9c91 feat(signal-forms): add support for resetting forms
Introduce a `reset()` method that restores the touched and dirty signals
of a field and its descendants to their initial states. `reset()` may be
called on the root `form()` field to reset the entire form.
2025-07-23 11:56:38 -07:00
Miles Malerba e96d9e3e51 refactor(signal-forms): rework metadata API (#62713)
* rename metadata key types

* fix metadata reduce to not confuse item and acc types

* remove defineComputed, defineResource

* rename define and meatdata logic binding fns

* move setMetadata next to addToMetadata

* address comments

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* pattern cleanup

* rename metadata concept to property

* consolidate field property state

* rename metadata.ts to property.ts

* add 2-param variant of property rule

* rename getDefault to getInitial for alignment with standard reduce

* address ai review

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-21 22:10:30 -07:00
Miles Malerba 04ba24eeeb fix bad rebase 2025-07-18 19:02:26 +00:00
Miles Malerba eefc3896f8 docs(signal-forms): add some tsdoc for async validation api (#62693)
* docs(signal-forms): add some tsdoc for async validation api

* address ai review

* address feedback
2025-07-18 11:20:06 -07:00
Miles Malerba 1243113feb Review of api/validators (#62659)
* Review of api/validators

* improve ergonomics of errors option on validators

Improves the ergonomics of the `errors` option offered by the standard
validators.
1. Renames it to `error` because in the majority of cases it will be
   used to return a single `ValidationError`.
2. Allows directly using a constant `ValidationError` insetad of having
   to create a function like `() => ValidationError.custom()`

* handle NaN case in min/max

* switch ValidationError to use camelCase names

* memoize result of logic functions

* allow any value with length or size for minlength/maxlength

* fix typos

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* address feedback

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-18 11:19:19 -07:00
Miles Malerba 63b4448a05 refactor(signal-forms): unify how users access metadata added with define and metadata (#62656)
* refactor(signal-forms): Replace DataKey with StaticMetadataKey

* Remove `data` from the `FieldState`, it is now accessed via `metadata`

* fix typo

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-17 17:12:35 -07:00
Miles Malerba ebb678616a feat(signal-forms): allow validation logic to return null and false (#62657)
* feat(signal-forms): allow validation logic to return null and false

* address feedback

* address feedback
2025-07-16 15:05:45 -07:00
Miles Malerba 74d83f4f3d refactor(signal-forms): more ergonomic metadata key creation (#62653)
* refactor(signal-forms): more ergonomic metadata key creation

* address feedback and add doc comments

* address ai feedback
2025-07-16 09:30:25 -07:00
Miles Malerba 2648e75441 refactor(signal-forms): remove error logic function (#62634)
We have gotten feedback from multiple sources that the distinction
between `error` and `validate` is confusing. Removing `error` for now
and we will explore alternate method signatures for `validate` instead
in future PRs
2025-07-15 13:11:48 -07:00
Miles Malerba 07939d671c cleanup unnecessary cast (#62635) 2025-07-15 13:04:08 -07:00
Miles Malerba 372f6559db refactor(signal-forms): use discriminated union for errors (#62446)
* refactor(signal-forms): use discriminated union for errors

* incorporate feedback

* Update packages/forms/experimental/src/api/validation_errors.ts

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* fix test

* refactor: base the union on real classes

* address ai review

* cleanup

* address feedback

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-15 11:26:11 -07:00
Kirill Cherkashin f8f0af5310 fix(forms): drop unused import (#62551)
This is also incorrect and breaks things

Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-07-09 12:41:28 -04:00
DESKTOP-V2DGHM1\Icecream e539e16073 docs(signal-forms): fix typos in example code (chandlerfang@gmail.com) 2025-07-08 17:42:00 -07:00
Miles Malerba bb12572d8e fix(signal-forms): fix broken test (#62532) 2025-07-08 16:27:19 -04:00
Miles Malerba 68643d2ddc feat(signal-forms): Add key & index support in FieldContext (#62442)
* feat(signal-forms): Add key & index support in FieldContext

* WIP: add PathKind type param to FieldPath

* WIP: add PathKind type param to logic functions

* fix remaining issues

* schema must take a root path kind

* add @template doc for TPathKind

* Apply suggestion from @leonsenft

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

* address comments

* address ai code review

* address comments

---------

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
2025-07-08 15:11:59 -04:00
Miles Malerba fead94d4ec standard schema support (#61438)
* add standard schema support

* move node tests under test/node

* Apply suggestions from code review

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>

* Update packages/forms/experimental/test/web/standard_schema.spec.ts

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* fix return type docs

---------

Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-07-01 16:30:23 -07:00
Leon Senft 090f7a38f1 docs(forms): fix typos (#62348) 2025-06-30 09:28:18 -07:00
Miles Malerba 92e7a45d43 Migrate to LogicNodeBuilder & LogicNode (#62220)
* migrate to logic node 2

* add some additional tests for predefined schemas

* fix resolve behavior

* tests and fixes for recursive logic

* post-rebase fixup

* add test for recursive logic w/ predicates

* fix predicates in recursive logic

* misc cleanup

* compile schemas once per form

* add test for recursive logic with arrays

* fix typo

* address review comments
2025-06-26 14:33:56 -07:00
Miles Malerba 3fa15e7bb9 refactor: add a new version of the logic node (#61079)
* refactor: add a new version of the logic node

Adds a new version of logic node that is created via builders. The
builder / actual logic node split allows the final logic for a node to
be lazily merged together.

My plan is to eventually swap this in for FieldLogicNode and have the
schema methods (`validate`, `apply`, etc.) work with the builders. The
FieldNode will then build its logic when it is first created.

This should be the other main piece aside from relative paths which is
needed to make self-referential logic work.

* refactor: maintain predicates as list

* refactor: refactor builder api

* refactor: defer predicate list expansion until merge time

* post-rebase fixup

* add support for new logic since last rebase

* add tsdoc
2025-06-23 09:08:59 -07:00
Kirill Cherkashin a833eb283f Forms: Always use provided injector on the form level (#62051)
Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-18 14:23:24 -04:00
Kirill Cherkashin 54d1d3e453 Forms: Allow dynamically disabling built-in validators by returning u… (#62121)
* Forms: Allow dynamically disabling built-in validators by returning undefined.

* fixup: drop binding built-in metadata to inputs for now

---------

Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-18 14:21:16 -04:00
Kirill Cherkashin acee6ce483 Forms: Minor clean up of tutorial v2 (#62049)
* Forms: Minor clean up of tutorial v2

* Forms: Minor clean up of tutorial v2

* Forms: Minor clean up of tutorial v2

* Forms: Minor clean up of tutorial v2

---------

Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-13 12:19:50 -04:00
Kirill Cherkashin 0b5592dcc9 Forms: Clarify array tracking (#62035)
Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-12 14:58:36 -04:00
Kirill Cherkashin d296791daa V2 tutorial (#62014)
* docs(forms): Add a tutorial for v2

* fixup: Address feedback

---------

Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-11 17:15:30 -04:00
Alex Rickabaugh 19ecc2f300 fix(forms): handle undefined nodes in resolve() 2025-06-09 11:30:48 -07:00
Alex Rickabaugh eb6c2ecf2a refactor(forms): split FieldNode into structure, validation, context, etc.
This commit should make the code much easier to follow by breaking apart
`FieldNode` into various pieces, each focused on a single concern.
2025-06-09 11:10:27 -07:00
Alex Rickabaugh 14587546aa fix(forms): properly handle fields moving around within arrays
`FieldNode`s were previously constructed with a static 'key', used for a few
purposes including `.value` operations (read/write). This meant that when a
`FieldNode` moved in an array via the tracking mechanism, it would be
resolved via the new path, but still write to the old path (a major bug).

This commit fixes the issue by introducing a proper reactive concept for the
field's current property key in the parent, which changes as the field moves
around.
2025-06-09 11:10:27 -07:00
Alex Rickabaugh 5fd1b8fb77 refactor(forms): properly handle dynamic keyed FieldNodes 2025-06-09 11:10:27 -07:00
Alex Rickabaugh 3a7e531526 refactor(forms): undefined model values don't have fields
Adjust the behavior and typing of forms when a model value is `undefined`.
In such cases, the form is not considered to have a field for that key of
the model, and navigation attempts should return `undefined`. The type
should also reflect the field's potential non-existence, as well as
correctly narrow the type when the field is non-null asserted.
2025-06-09 11:10:27 -07:00
Kirill Cherkashin d9533445dd refactor(forms): Make imports consistent (#61908)
Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-05 15:40:46 -04:00
michael-small 5e675b1354 docs: align jsdocs of experimental sf logic & structure functions (#61887) 2025-06-05 09:44:39 -04:00
Miles Malerba 00f9d5a321 switch from $state to () (#61834) 2025-06-04 10:46:36 -07:00
Kirill Cherkashin d2bca12049 The rest of the built-in validators (#61758)
* forms: Add remaining build-in validators

* fixup: Address feedback

* fixup: Clean up

---------

Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-06-04 12:50:49 -04:00
Miles Malerba 7ecec96d3b refactor(forms): update status states based on prev discussion (#61852)
As discussed:
- Rename `.hasPendingValidators()` to just `.pending()`
- Eliminate `.status()` as a public API

Note:
- `.status()` retained internally as part of the implementation
- renamed the `'pending'` status option to `'unknown'` to avoid
  confusion with `.pending()`
2025-06-03 13:28:55 -07:00
Miles Malerba d19cb9e485 inherit disabled reason to children (#61829)
* inherit disabled reason to children

* update docs
2025-06-03 11:59:22 -07:00
Alex Rickabaugh 69f18f99d9 refactor(forms): rename FieldDirective to Control 2025-06-03 08:22:14 -07:00
Miles Malerba 7ebf93f9f8 update docs (#61711)
* update docs

Updates docs to:
- Show usage of the `schema()` function
- Mention need to run in injection context (& fix examples)

Also updates the `form` API to accept both `form(model, options)` and
`form(model, schema)`

* fixup! update docs

* add a brief explanation of each logic function

* add docs for async validation

* remove defaultValue signatures of validateHttp

* Update packages/forms/experimental/docs/signal-forms.md

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>

* fixup! Update packages/forms/experimental/docs/signal-forms.md

---------

Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
2025-05-30 09:59:28 -07:00
Alex Rickabaugh 7b68bf01fb feat(forms): support automatic array tracking
This feature adds a symbol tag to values in arrays, which is used to
automatically track those values as they move around in the array. It's one
way of achieving array tracking without a lot of overhead or configuration.

Primitive values can't be patched like this, and are still tracked by index.
2025-05-28 10:16:19 -07:00
Kirill Cherkashin 6410539818 Adding min and max validators. (#61474)
* feat(forms): Adding min and max validators.
Creating a separate file for built-in validators.
2025-05-28 09:30:18 -04:00
Miles Malerba e3221efa7f add shorthand to validate with http resource (#61707) 2025-05-27 13:55:45 -07:00
Kirill Cherkashin ecb8db04e4 forms: Drop data from context, as it is already on the state (#61646)
Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-05-27 12:47:09 -04:00
Miles Malerba 37c5ff61c6 refactor validation status (#61546) 2025-05-22 15:52:46 -07:00
Kirill Cherkashin ea589628a7 Drop resolve from everywhere (#61614)
* forms: Drop resolve, and use the new API everywhere

* fixup: Minor cleanup

---------

Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-05-22 11:27:36 -04:00
Kirill Cherkashin e221067e1c Adding state/field/valueOf/StateOf/FieldOf to field context (#61378)
* feat(forms): Adding state/field/valueOf/StateOf/FieldOf to field context
I'm currently keeping `resolve` so we don't have to do big changes until we finalize the API.
2025-05-21 10:59:33 -04:00
Kirill Cherkashin e61da79b3c feat(forms): Support marking a field as dirty (#61183)
Co-authored-by: kirjs <kirjs@users.noreply.github.com>
2025-05-20 15:52:23 -04:00
Miles Malerba aa318860db refactor(forms): add back schema function (#61379) 2025-05-20 11:58:40 -07:00
Alex Rickabaugh 359eb4df1b Merge branch 'main' of github.com:angular/angular into prototype/signal-forms 2025-05-20 09:57:25 -07:00
Miles Malerba dc404f0a8e add support for readonly (#61369)
* add support for readonly

* skip validation on readonly fields
2025-05-15 12:33:21 -07:00
Alex Rickabaugh 383543e5f9 wip: async validation! 2025-05-14 14:00:37 -07:00
Miles Malerba 0c99e56f97 add default touched event logic for ui controls (#61288) 2025-05-13 08:49:00 -07:00
Alex Rickabaugh a12a9fe4f9 wip: injection, data deps, resources 2025-05-12 11:42:15 -07:00
Miles Malerba 8b5aeebb54 test: switch tests to jasmine_node_test (#61074) 2025-04-30 16:03:48 -04:00
Miles Malerba 02f26cf56a Revert "Async WIP"
This reverts commit 94e1ee9eef.
2025-04-28 21:03:55 +00:00
kirjs 94e1ee9eef Async WIP 2025-04-28 16:57:31 -04:00
Miles Malerba 9385dc457c refactor: use relative FieldPathNodes (#60791)
* refactor: use relative FieldPathNodes

Switch `apply` behavior to create a brand new root path whenever a new
schema is applied. This effectively changes our `FieldPathNode` concept
from an absolute path to a relative path.

To record how the relative path nodes are connected, introduce a
`rootPaths` map to each logic node, and when we `apply`, copy all roots
from the sub-schema to the root of the current schema. This results in
the top-level schema having a mapping to all roots under it, allowing it
to construct the full absolute path.

Note: this breaks the current resolve logic, resolve needs to be
rewritten to understand the new path structure.

* refactor: merge logic nodes

Adds a method to merge one logic node into another. This allows them to
be built up separaterly and then joined, instead of always applying
rules to the same logic instance.

* fix: update resolve to handle relative paths

* fix: preserve predicates while merging

* refactor: cleanup code organization

* refactor: move subroots concept from logic to path

* refactor: Differentiate PathNode vs RootPathNode

* refactor: Optimize resolve logic

Optimize the resolve logic by only calling `getChild` from the common
ancestor field rather than the root field of the entire form. This
ensures we do not overreact to changes that won't affect the resolved
field.
2025-04-22 09:39:25 -07:00
kirjs 8b4d217466 tutorial: Move list of features to the doc 2025-04-16 13:19:19 -04:00
kirjs 16d637eb6a Expose metadata through public api 2025-04-16 12:35:13 -04:00
kirjs 05a4299ea6 Expose metadata through public api 2025-04-16 12:06:13 -04:00
kirjs 1b607655b6 Add hidden section to the tutorial
Improve applyWhen explanation
Move metadata stuff to public API
2025-04-16 11:53:02 -04:00
kirjs 9089293adf Add spaces to properly format code things 2025-04-11 12:45:27 -04:00
kirjs e91434fd71 Space feedback form description better, so markdown picks it up as code 2025-04-11 08:44:21 -04:00
kirjs 01e18cde8e Final tutorial polish 2025-04-10 18:17:56 -04:00
kirjs 3712624dfa Update tutorial and resolve TODOs 2025-04-10 15:50:00 -04:00
kirjs 7f341416f9 Add forms tutorial 2025-04-10 15:29:04 -04:00
kirjs d962dc9be1 Add Validator<T> type 2025-04-08 12:50:39 -04:00
kirjs 0cb0cd47af Only export public stuff 2025-04-07 13:38:41 -04:00
kirjs 6d21572825 Rexport everything through public_api.ts 2025-04-07 11:51:26 -04:00
kirjs 1dc28b05c0 Update readme with things that are not yet supported 2025-04-04 15:59:29 -04:00
Miles Malerba aee6ab93d6 Update docs to reflect renamed API concepts (#60734)
* Update docs to reflect renamed API concepts

* fixup! Update docs to reflect renamed API concepts

* Update docs to reflect removal of `schema()` function
2025-04-04 08:32:53 -07:00
Miles Malerba df80ab9d4c Simplify concept of schemas to just a type (#60732) 2025-04-03 16:49:51 -07:00
Miles Malerba 1be49f70d0 More followup renaming 2025-04-03 21:02:59 +00:00
Miles Malerba f142d48b8d Update docs and names to better align with the new API names 2025-04-03 20:31:33 +00:00
Miles Malerba 95fb3b4c14 Rename APIs as discussed last session 2025-04-03 18:24:49 +00:00
kirjs 2bf5732026 Move more changes 2025-04-03 12:38:07 -04:00
Miles Malerba 2a52ce8019 Add disabled reason support 2025-04-03 16:26:42 +00:00
kirjs 0f7c4338dc Fix imports in node spec 2025-04-03 12:16:13 -04:00
kirjs 29384b5344 Bring back node spec 2025-04-03 12:07:38 -04:00
Miles Malerba 86f3816abd Add some user docs 2025-04-03 15:52:04 +00:00
kirjs c71cc659f9 Make sure all files have licence header. 2025-04-02 13:29:12 -04:00
kirjs ee492c979b Move rule-based forms prototype into the main repo.
Note: this is a prototype, and is still work in progress

Get rid of old stuff
2025-04-02 13:21:24 -04:00
kirjs b167365bde Allow marking things as not dirty 2025-03-05 15:54:33 -05:00
Miles Malerba 069282f462 wip ui test 2025-03-03 19:42:18 +00:00
Alex Rickabaugh 51e79aaf73 impl: validation & errors
Propagation of validation and errors (untested)
2025-02-28 14:02:07 -08:00
Miles Malerba 001b0ba185 cleanup some old stuff 2025-02-28 01:06:19 +00:00
Miles Malerba 0ee9c7037b add form proxy 2025-02-28 01:00:15 +00:00
Miles Malerba 4f3ea59c1e work on schema for dynamic els 2025-02-26 22:08:27 +00:00
Alex Rickabaugh 1ff640dfb3 impl: working node, arrays, & logic 2025-02-24 13:59:42 -08:00
Miles Malerba 32cc250080 nested arrays test 2025-02-21 22:18:46 +00:00
Miles Malerba 75d394eb2e more thinking & prototyping 2025-02-21 21:59:40 +00:00
Alex Rickabaugh 794e80b057 refactor: sketch outline of FormNode (replacement for FormFieldImpl) 2025-02-19 14:03:29 -08:00
Miles Malerba 7d37cf7877 minor cleanup for engine 2025-02-04 00:14:35 +00:00
Miles Malerba 85f8db7539 directive stand-in for the field concept 2025-02-03 23:33:58 +00:00
Alex Rickabaugh 7ce2640a28 refactor: add linked signal to FormFieldImpl 2025-01-29 14:05:38 -08:00
Miles Malerba 68a4bf35e9 refactoring pass & metadata 2025-01-28 17:40:25 +00:00
Alex Rickabaugh 882c0ea8a1 refactor(forms): add engine & tests 2025-01-27 13:58:17 -08:00
Miles Malerba 1c8ad18dd6 cleanup some earlier prototyping 2025-01-24 22:43:38 +00:00
Miles Malerba afdbeae46c add some ideas about schema merging 2025-01-21 18:05:24 +00:00
Miles Malerba ebecdea206 prototype2: dynamic objects & zod 2025-01-21 18:01:21 +00:00
Miles Malerba 47333e4aa0 add another prototype idea 2025-01-12 21:25:18 +00:00
kirjs 68eba763fe Add custom schema experiments 2025-01-07 14:12:38 -05:00
kirjs ab17a84da6 Actually mention signals in readme :) 2024-12-26 14:44:39 -05:00
kirjs b2d971b143 Add my thoughts on potential signal forms API 2024-12-26 14:36:54 -05:00
Miles Malerba a94d0db4db refactor: update idea1 into a working minimal prototype 2024-12-23 23:29:42 +00:00
Miles Malerba fb67fc2a0e add an idea for binding field to elements 2024-12-15 22:17:40 +00:00
Miles Malerba 8e830f942f refactor: cleanup idea1 and add notes 2024-12-15 19:05:46 +00:00
Miles Malerba 405f2f0a41 refactor: noop impl of initial concept 2024-12-10 23:40:20 +00:00
Alex Rickabaugh 139a4afdbd refactor: move PACKAGE.md to README.md and include entrypoint in build 2024-12-10 14:18:56 -08:00
Miles Malerba 8550bd0683 refactor: remove forms-experimental, moved to forms/experimental 2024-12-10 21:19:08 +00:00
Miles Malerba 78505746ee refactor: add experimental forms package
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
2024-12-10 19:24:08 +00:00
Miles Malerba d29ced91a1 refactor: add experimental forms package
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
2024-12-10 01:16:54 +00:00
3493 changed files with 327371 additions and 331393 deletions
+3 -12
View File
@@ -4,7 +4,7 @@
# https://github.com/bazelbuild/bazel/issues/8106
.git
node_modules
dist/
dist
# All integration test node_modules folders
integration/animations/node_modules
@@ -35,11 +35,9 @@ integration/standalone-bootstrap/node_modules
integration/terser/node_modules
integration/trusted-types/node_modules
integration/typings_test_rxjs7/node_modules
integration/legacy-animations/node_modules
integration/legacy-animations-async/node_modules
integration/typings_test_ts59/node_modules
integration/typings_test_ts58/node_modules
modules/ssr-benchmarks/node_modules
vscode-ng-language-service/integration/project/dist/
# For rules_js
adev/node_modules
@@ -63,14 +61,7 @@ packages/platform-server/node_modules
packages/platform-browser-dynamic/node_modules
packages/router/node_modules
packages/zone.js/node_modules
packages/zone.js/test/typings/node_modules
packages/upgrade/node_modules
packages/benchpress/node_modules
packages/service-worker/node_modules
packages/zone.js/test/typings/node_modules
packages/zone.js/node_modules
tools/bazel/rules_angular_store/node_modules
vscode-ng-language-service/node_modules
vscode-ng-language-service/server/node_modules
vscode-ng-language-service/integration/pre_standalone_project/node_modules
vscode-ng-language-service/integration/project/node_modules
+3 -4
View File
@@ -69,9 +69,6 @@ build:release --stamp
# Snapshots should also be stamped with version control information.
build:snapshot-build --workspace_status_command="pnpm --silent ng-dev release build-env-stamp --mode=snapshot"
build:snapshot-build --stamp
# @angular/language-server package depends directly on other Angular framework packages.
# This flag ensures that the language server uses the snapshot-built framework packages from the repository.
build:snapshot-build --//:enable_language_server_snapshot_repo_deps
# Angular DevTools for Firefox releases *cannot* be stamped and `--config snapshot-build-firefox` is a no-op.
# This is because Mozilla requires add-on source code to be uploaded and then they manually reproduce the build.
@@ -143,7 +140,7 @@ 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
common --incompatible_allow_tags_propagation
common --experimental_allow_tags_propagation
# Disable network access in the sandbox by default. To enable network access
# for a particular target, use:
@@ -172,6 +169,8 @@ test:saucelabs --flaky_test_attempts=1
# --ng_perf will ask the Ivy compiler to produce performance results for each build.
build --flag_alias=ng_perf=//packages/compiler-cli:ng_perf
# --full_build_adev will run adev build/serve in a full mode, performing prerendering and DCE.
build --flag_alias=full_build_adev=//adev:full_build_adev
####################################################
# User bazel configuration
+1 -1
View File
@@ -1 +1 @@
8.4.2
7.6.1
@@ -12,7 +12,6 @@ esbuild_checked_in(
entry_point = ":lib/main.mts",
external = [
"undici",
"pnpapi",
],
metafile = False,
platform = "node",
+14 -14
View File
@@ -1,4 +1,4 @@
import {mkdtemp, readFile, rm, writeFile} from 'node:fs/promises';
import {cp, mkdtemp, readFile, rm, writeFile} from 'node:fs/promises';
import {Deployment} from './deployments.mjs';
import {join} from 'node:path';
@@ -9,7 +9,7 @@ import {getCredentialFilePath} from './credential.mjs';
export async function deployToFirebase(
deployment: Deployment,
configPath: string,
stagingDir: string,
distDirPath: string,
) {
if (deployment.destination == undefined) {
console.log(`No deployment necessary for docs created from: ${deployment.branch}`);
@@ -18,33 +18,37 @@ export async function deployToFirebase(
console.log('Preparing for deployment to firebase...');
const deployConfigPath = join(stagingDir, 'firebase.json');
const tmpDeployDir = await mkdtemp(join(tmpdir(), 'deploy-directory'));
const deployConfigPath = join(tmpDeployDir, 'firebase.json');
const config = JSON.parse(await readFile(configPath, {encoding: 'utf-8'})) as {
hosting: {public: string};
};
config['hosting']['public'] = './browser';
config['hosting']['public'] = './dist';
await writeFile(deployConfigPath, JSON.stringify(config, null, 2));
await cp(distDirPath, join(tmpDeployDir, 'dist'), {recursive: true});
spawnSync(`chmod 777 -R ${tmpDeployDir}`, {encoding: 'utf-8', shell: true});
firebase(
`target:clear --config ${deployConfigPath} --project angular-dev-site hosting angular-docs`,
stagingDir,
tmpDeployDir,
);
firebase(
`target:apply --config ${deployConfigPath} --project angular-dev-site hosting angular-docs ${deployment.destination}`,
stagingDir,
tmpDeployDir,
);
firebase(
`deploy --config ${deployConfigPath} --project angular-dev-site --only hosting --non-interactive`,
stagingDir,
tmpDeployDir,
);
firebase(
`target:clear --config ${deployConfigPath} --project angular-dev-site hosting angular-docs`,
stagingDir,
tmpDeployDir,
);
await rm(stagingDir, {recursive: true});
await rm(tmpDeployDir, {recursive: true});
}
export async function setupRedirect(deployment: Deployment) {
@@ -99,7 +103,7 @@ export async function setupRedirect(deployment: Deployment) {
}
function firebase(cmd: string, cwd?: string) {
const {status} = spawnSync('npx', `-y firebase-tools@13.15.1 ${cmd}`.split(' '), {
spawnSync('npx', `-y firebase-tools@13.15.1 ${cmd}`.split(' '), {
cwd,
encoding: 'utf-8',
shell: true,
@@ -109,8 +113,4 @@ function firebase(cmd: string, cwd?: string) {
GOOGLE_APPLICATION_CREDENTIALS: getCredentialFilePath(),
},
});
if (status !== 0) {
console.error('Firebase command failed, see log above for details.');
process.exit(status);
}
}
@@ -9,7 +9,6 @@ export interface Deployment {
to: string;
};
destination?: string;
servingUrl: string;
}
export type Deployments = Map<string, Deployment>;
@@ -32,7 +31,6 @@ export async function getDeployments(): Promise<Deployments> {
docSites.set(branch.name, {
branch: branch.name,
destination: `v${branch.version.major}-angular-dev`,
servingUrl: `https://v${branch.version.major}.angular.dev/`,
});
});
@@ -42,14 +40,12 @@ export async function getDeployments(): Promise<Deployments> {
from: `v${releaseTrains.latest.version.major}-angular-dev`,
to: 'https://angular.dev',
},
servingUrl: 'https://angular.dev/',
destination: 'angular-dev-site',
});
if (releaseTrains.releaseCandidate) {
docSites.set(releaseTrains.next.branchName, {
branch: releaseTrains.next.branchName,
servingUrl: 'https://next.angular.dev/',
});
docSites.set(releaseTrains.releaseCandidate.branchName, {
@@ -59,13 +55,11 @@ export async function getDeployments(): Promise<Deployments> {
from: `v${releaseTrains.releaseCandidate.version.major}-angular-dev`,
to: 'https://next.angular.dev',
},
servingUrl: 'https://next.angular.dev/',
});
} else {
docSites.set(releaseTrains.next.branchName, {
branch: releaseTrains.next.branchName,
destination: 'next-angular-dev',
servingUrl: 'https://next.angular.dev/',
});
}
+10 -14
View File
@@ -2,18 +2,19 @@ import {getInput, setFailed} from '@actions/core';
import {context} from '@actions/github';
import {deployToFirebase, setupRedirect} from './deploy.mjs';
import {getDeployments} from './deployments.mjs';
import {generateSitemap} from './sitemap.mjs';
import {assertValidGithubConfig, AuthenticatedGitClient, getConfig} from '@angular/ng-dev';
import {AuthenticatedGitClient, GithubConfig, setConfig} from '@angular/ng-dev';
import {githubReleaseTrainReadToken} from './credential.mjs';
import {spawnSync} from 'child_process';
import {cp, mkdtemp} from 'fs/promises';
import {tmpdir} from 'os';
import {join} from 'path';
const refMatcher = /refs\/heads\/(.*)/;
async function deployDocs() {
getConfig([assertValidGithubConfig]);
setConfig({
github: {
mainBranchName: 'main',
name: 'angular',
owner: 'angular',
} as GithubConfig,
});
AuthenticatedGitClient.configure(githubReleaseTrainReadToken);
@@ -27,11 +28,7 @@ async function deployDocs() {
const currentBranch = matchedRef[1];
const configPath = getInput('configPath');
const stagingDir = await mkdtemp(join(tmpdir(), 'deploy-directory'));
// Copy all files from the distDir into stagingDir and modify the permissions for editing
await cp(getInput('distDir'), stagingDir, {recursive: true});
spawnSync(`chmod 777 -R ${stagingDir}`, {encoding: 'utf-8', shell: true});
const distDir = getInput('distDir');
const deployment = (await getDeployments()).get(currentBranch);
if (deployment === undefined) {
@@ -60,8 +57,7 @@ async function deployDocs() {
console.log(` To: ${deployment.redirect.to}`);
}
await generateSitemap(deployment, stagingDir);
await deployToFirebase(deployment, configPath, stagingDir);
await deployToFirebase(deployment, configPath, distDir);
await setupRedirect(deployment);
}
@@ -1,33 +0,0 @@
import {Deployment} from './deployments.mjs';
import {join} from 'path';
import {readFileSync, writeFileSync} from 'fs';
export async function generateSitemap(deployment: Deployment, distDir: string) {
const servingUrlWithoutEndingSlash = deployment.servingUrl.endsWith('/')
? deployment.servingUrl.slice(0, -1)
: deployment.servingUrl;
/** Timestamp string used to of the last file update. */
const lastModifiedTimestamp = new Date().toISOString();
/** An object containing all of the routes available within the application. */
const routes = JSON.parse(readFileSync(join(distDir, 'prerendered-routes.json'), 'utf-8'));
/** The generated sitemap string. */
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${Object.keys(routes.routes)
.map((route) => {
const routeWithoutLeadingSlash = route.startsWith('/') ? route.slice(1) : route;
return `
<url>
<loc>${servingUrlWithoutEndingSlash}/${routeWithoutLeadingSlash}</loc>
<lastmod>${lastModifiedTimestamp}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
`;
})
.join('')}
</urlset>`;
writeFileSync(join(distDir, 'browser', 'sitemap.xml'), sitemap, 'utf-8');
console.log(`Generated sitemap with ${Object.keys(routes.routes).length} entries.`);
}
File diff suppressed because one or more lines are too long
@@ -2,7 +2,6 @@
"compilerOptions": {
"module": "NodeNext",
"target": "ES2021",
"types": ["node"],
"lib": ["es2021"],
"experimentalDecorators": true,
"strict": true,
@@ -12,5 +11,6 @@
"noImplicitReturns": true,
"declaration": true,
"sourceMap": true
}
}
+2 -2
View File
@@ -5,9 +5,9 @@ runs:
using: 'composite'
steps:
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/saucelabs@80db036355181684ed07021a175e9f039190d3d6
- name: Starting Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh run &
+7 -7
View File
@@ -15,22 +15,22 @@ permissions: read-all
jobs:
adev-build:
runs-on: ubuntu-latest-8core
runs-on: ubuntu-latest
if: |
(github.event.action == 'labeled' && github.event.label.name == 'adev: preview') ||
(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@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
run: pnpm bazel build //adev:build.production --config=snapshot-build
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@90bba8b673b749ce67760b4d993b9b421adac30f
- name: Build adev to ensure it continues to work
run: pnpm bazel build //adev:build --full_build_adev --config=release
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@80db036355181684ed07021a175e9f039190d3d6
with:
workflow-artifact-name: 'adev-preview'
pull-number: '${{github.event.pull_request.number}}'
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
token: '${{secrets.GITHUB_TOKEN}}'
@@ -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@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@80db036355181684ed07021a175e9f039190d3d6
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -12,11 +12,10 @@ permissions:
jobs:
assistant_to_the_branch_manager:
runs-on: ubuntu-latest
if: github.event.repository.fork == false
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: angular/dev-infra/github-actions/branch-manager@80db036355181684ed07021a175e9f039190d3d6
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+4 -4
View File
@@ -19,7 +19,7 @@ jobs:
exit: true
# Indicate that the benchmark command was received.
- uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{github.event.comment.id}}
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
@@ -28,7 +28,7 @@ jobs:
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
id: comment-branch
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Specify repository as the PR branch might be from a fork.
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
@@ -38,7 +38,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
with:
bazelrc: ./.bazelrc.user
@@ -53,7 +53,7 @@ jobs:
id: benchmark
name: Running benchmark
- uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
issue-number: ${{github.event.issue.number}}
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
+51 -58
View File
@@ -21,7 +21,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Check code lint
@@ -39,13 +41,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
disable-package-manager-cache: true
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -55,23 +57,24 @@ jobs:
- name: Test build
run: pnpm devtools:build:chrome
- name: Cypress run
uses: cypress-io/github-action@7ef72e250a9e564efb4ed4c2433971ada4cc38b4 # v6.10.4
uses: cypress-io/github-action@b8ba51a856ba5f4c15cf39007636d4ab04f23e3c # v6.10.2
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
wait-on: 'http://localhost:4200'
wait-on-timeout: 300
install: false
test:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -80,14 +83,16 @@ jobs:
run: pnpm test:ci
integration-tests:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -97,14 +102,14 @@ jobs:
adev:
runs-on:
labels: ubuntu-latest
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -112,51 +117,42 @@ jobs:
- name: Run tests
run: pnpm bazel test //adev/...
- name: Build adev in fast mode to ensure it continues to work
run: pnpm bazel build //adev:build
vscode-ng-language-service:
runs-on:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm bazel test //vscode-ng-language-service/...
run: pnpm bazel build //adev:build --config=release
publish-snapshots:
runs-on:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
- run: pnpm build
- run: ./scripts/ci/publish-snapshot-build-artifacts.sh
- run: ./scripts/ci/publish-build-artifacts.sh
zone-js:
runs-on:
labels: ubuntu-latest
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -190,12 +186,14 @@ jobs:
- run: pnpm -C packages/zone.js/test/typings test
# saucelabs:
# runs-on: ubuntu-latest
# runs-on: ubuntu-latest-4core
# env:
# SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
# steps:
# - name: Initialize environment
# uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b5a3609f89c06eb4037dce22a93641213a5d1508
# with:
# cache-node-modules: true
# - name: Install node modules
# run: pnpm install --frozen-lockfile
# - uses: ./.github/actions/saucelabs-legacy
@@ -203,27 +201,22 @@ jobs:
adev-deploy:
needs: [adev]
if: needs.adev.result == 'success'
runs-on: ubuntu-latest-8core
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
# `snapshot-build` config is used to stamp the exact version with sha in the footer.
run: pnpm bazel build //adev:build.production --config=snapshot-build
- name: Build adev to ensure it continues to work
run: pnpm bazel build //adev:build --full_build_adev --config=release
- name: Deploy to firebase
uses: ./.github/actions/deploy-docs-site
with:
serviceKey: ${{ secrets.ANGULAR_DEV_SITE_DEPLOY }}
githubReleaseTrainReadToken: ${{ secrets.DOCS_DEPLOY_GITHUB_RELEASE_TRAIN_TOKEN }}
configPath: 'adev/firebase.json'
distDir: 'dist/bin/adev/dist'
- name: Update Algolia synonym record
run: pnpm tsx adev/scripts/synonyms/update-synonyms.mts
env:
ALGOLIA_KEY: ${{ secrets.ALGOLIA_SYNONYM_MANAGER }}
distDir: 'dist/bin/adev/dist/browser'
@@ -1,59 +0,0 @@
name: Update ADEV Cross Repo Docs
on:
workflow_dispatch:
inputs: {}
push:
branches:
- 'main'
- '[0-9]+.[0-9]+.x'
# Declare default permissions as read only.
permissions:
contents: read
jobs:
update_adev_cross_repo_docs:
name: Update Cross Repo ADEV Docs
if: github.repository == 'angular/angular'
runs-on: ubuntu-latest
# Prevents multiple concurrent runs of this workflow for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout the repository
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
# an authenticated URL.
persist-credentials: false
- name: Generate Docs
run: node adev/scripts/update-cross-repo-docs/index.mjs
env:
ANGULAR_READONLY_GITHUB_TOKEN: ${{ secrets.READONLY_GITHUB_TOKEN }}
- name: Create a PR (if necessary)
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
with:
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
push-to-fork: 'angular-robot/angular'
delete-branch: true
maintainer-can-modify: false
branch: docs-cross-repo-update-${{github.ref_name}}
committer: Angular Robot <angular-robot@google.com>
author: Angular Robot <angular-robot@google.com>
title: 'docs: update cross-repo adev docs [${{github.ref_name}}]'
body: |
Updated Angular adev cross repo docs files.
labels: |
action: merge
area: docs
target: automation
commit-message: |
docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
+4 -4
View File
@@ -12,14 +12,14 @@ jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- uses: angular/dev-infra/github-actions/pull-request-labeling@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: angular/dev-infra/github-actions/pull-request-labeling@80db036355181684ed07021a175e9f039190d3d6
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
post_approval_changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- uses: angular/dev-infra/github-actions/post-approval-changes@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: angular/dev-infra/github-actions/post-approval-changes@80db036355181684ed07021a175e9f039190d3d6
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -13,8 +13,8 @@ jobs:
statuses: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- uses: angular/dev-infra/github-actions/google-internal-tests@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: angular/dev-infra/github-actions/google-internal-tests@80db036355181684ed07021a175e9f039190d3d6
with:
run-tests-guide-url: http://go/angular-g3sync-start
github-token: ${{ secrets.GITHUB_TOKEN }}
+7 -5
View File
@@ -8,20 +8,22 @@ jobs:
# Bazel saucelabs job resides in `manual.yml` because it's currently unstable, but
# kept as "runnable" for debugging/stabilization effort purposes.
bazel-saucelabs:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
env:
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/saucelabs@80db036355181684ed07021a175e9f039190d3d6
- name: Set up Sauce Tunnel Daemon
run: pnpm bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@90bba8b673b749ce67760b4d993b9b421adac30f
- uses: angular/dev-infra/github-actions/unified-status-check@80db036355181684ed07021a175e9f039190d3d6
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+4 -4
View File
@@ -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@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- id: workflows
@@ -36,16 +36,16 @@ jobs:
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
# We utilize the google-github-actions/auth action to allow us to get an active credential using workflow
# identity federation. This allows us to request short lived credentials on demand, rather than storing
# credentials in secrets long term. More information can be found at:
# https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform
- uses: 'google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093' # v3
- uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' # v2
with:
project_id: 'internal-200822'
workload_identity_provider: 'projects/823469418460/locations/global/workloadIdentityPools/measurables-tracking/providers/angular'
+41 -41
View File
@@ -19,7 +19,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Check code lint
@@ -37,7 +39,7 @@ jobs:
- name: Check code format
run: pnpm ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
- name: Check Package Licenses
uses: angular/dev-infra/github-actions/linting/licenses@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/linting/licenses@80db036355181684ed07021a175e9f039190d3d6
with:
allow-dependencies-licenses: 'pkg:npm/google-protobuf@'
@@ -45,13 +47,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
disable-package-manager-cache: true
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run unit tests
@@ -59,23 +61,24 @@ jobs:
- name: Test build
run: pnpm devtools:build:chrome
- name: Cypress run
uses: cypress-io/github-action@7ef72e250a9e564efb4ed4c2433971ada4cc38b4 # v6.10.4
uses: cypress-io/github-action@b8ba51a856ba5f4c15cf39007636d4ab04f23e3c # v6.10.2
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
wait-on: 'http://localhost:4200'
wait-on-timeout: 300
install: false
test:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-8core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run CI tests for framework
@@ -92,14 +95,16 @@ jobs:
retention-days: 1
integration-tests:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run integration CI tests for framework
@@ -107,46 +112,37 @@ jobs:
adev:
runs-on:
labels: ubuntu-latest
labels: ubuntu-latest-8core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm bazel test //adev/...
- name: Build adev in fast mode to ensure it continues to work
run: pnpm bazel build //adev:build
vscode-ng-language-service:
runs-on:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm bazel test //vscode-ng-language-service/...
run: pnpm bazel build //adev:build --config=release
zone-js:
runs-on:
labels: ubuntu-latest
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@80db036355181684ed07021a175e9f039190d3d6
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/setup@80db036355181684ed07021a175e9f039190d3d6
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@90bba8b673b749ce67760b4d993b9b421adac30f
uses: angular/dev-infra/github-actions/bazel/configure-remote@80db036355181684ed07021a175e9f039190d3d6
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: |
@@ -167,6 +163,8 @@ jobs:
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/test/closure/zone.closure.js ./packages/zone.js/build/test/zone.closure.mjs
# Install
- run: pnpm -C packages/zone.js install --frozen-lockfile
# Run zone.js tools tests
- run: pnpm -C packages/zone.js promisefinallytest
- run: pnpm -C packages/zone.js jest:test
@@ -176,12 +174,14 @@ jobs:
- run: pnpm -C packages/zone.js/test/typings test
# saucelabs:
# runs-on: ubuntu-latest
# runs-on: ubuntu-latest-4core
# env:
# SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
# steps:
# - name: Initialize environment
# uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b5a3609f89c06eb4037dce22a93641213a5d1508
# with:
# cache-node-modules: true
# - name: Install node modules
# run: pnpm install --frozen-lockfile
# - uses: ./.github/actions/saucelabs-legacy
+3 -3
View File
@@ -25,12 +25,12 @@ jobs:
steps:
- name: 'Checkout code'
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: 'Run analysis'
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: 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@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
sarif_file: results.sarif
@@ -0,0 +1,87 @@
name: Update ADEV Angular CDK APIs and CLI Help
on:
workflow_dispatch:
inputs: {}
push:
branches:
- 'main'
- '[0-9]+.[0-9]+.x'
# Declare default permissions as read only.
permissions:
contents: read
jobs:
update_adev_docs:
name: Update Angular CDK APIs and CLI Help (if necessary)
if: github.repository == 'angular/angular'
runs-on: ubuntu-latest
# Prevents multiple concurrent runs of this workflow for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout the repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
# an authenticated URL.
persist-credentials: false
# Angular CDK
- name: Generate CDK apis
run: node adev/scripts/update-cdk-apis/index.mjs
env:
ANGULAR_CDK_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CDK_BUILDS_READONLY_GITHUB_TOKEN }}
- name: Create a PR CDK apis (if necessary)
uses: peter-evans/create-pull-request@v7.0.8 # v7.0.8
with:
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
push-to-fork: 'angular-robot/angular'
delete-branch: true
maintainer-can-modify: false
branch: docs-update-cdk-apis-${{github.ref_name}}
committer: Angular Robot <angular-robot@google.com>
author: Angular Robot <angular-robot@google.com>
title: 'docs: update Angular CDK apis [${{github.ref_name}}]'
body: |
Updated Angular CDK api files.
labels: |
action: merge
area: docs
commit-message: |
docs: update Angular CDK apis
Updated Angular CDK api files.
# Angular CLI
- name: Reset to current GitHub ref
run: |
git reset --hard ${{ github.ref }}
- name: Generate CLI help
run: node adev/scripts/update-cli-help/index.mjs
env:
ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }}
- name: Create a PR CLI help (if necessary)
uses: peter-evans/create-pull-request@v7.0.8 # v7.0.8
with:
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
push-to-fork: 'angular-robot/angular'
delete-branch: true
maintainer-can-modify: false
branch: docs-update-cli-help-${{github.ref_name}}
committer: Angular Robot <angular-robot@google.com>
author: Angular Robot <angular-robot@google.com>
title: 'docs: update Angular CLI help [${{github.ref_name}}]'
body: |
Updated Angular CLI help contents.
labels: |
action: merge
area: docs
commit-message: |
docs: update Angular CLI help
Updated Angular CLI help contents.
+2
View File
@@ -16,7 +16,9 @@ pubspec.lock
!.devcontainer/recommended-devcontainer.json
!.devcontainer/recommended-Dockerfile
.settings/
.vscode/launch.json
.vscode/settings.json
.vscode/tasks.json
*.swo
*.swp
modules/.settings
-23
View File
@@ -1,23 +0,0 @@
/**
* The configuration for `ng-dev caretaker` commands.
*
* @type { import("@angular/ng-dev").CaretakerConfig }
*/
export const caretaker = {
g3SyncConfigPath: './.ng-dev/google-sync-config.json',
githubQueries: [
{
name: 'Merge Queue',
query: `is:pr is:open label:"action: merge"`,
},
{
name: 'Merge Assistance Queue',
query: `is:pr is:open label:"merge: caretaker note" label:"action: merge"`,
},
{
name: 'Initial Triage Queue',
query: `is:open no:milestone -draft:true`,
},
],
hasEmeaCaretaker: true,
};
+21
View File
@@ -0,0 +1,21 @@
import {CaretakerConfig} from '@angular/ng-dev';
/** The configuration for `ng-dev caretaker` commands. */
export const caretaker: CaretakerConfig = {
g3SyncConfigPath: './.ng-dev/google-sync-config.json',
githubQueries: [
{
name: 'Merge Queue',
query: `is:pr is:open label:"action: merge"`,
},
{
name: 'Merge Assistance Queue',
query: `is:pr is:open label:"merge: caretaker note" label:"action: merge"`,
},
{
name: 'Initial Triage Queue',
query: `is:open no:milestone -draft:true`,
},
],
caretakerGroup: 'angular-caretaker',
};
-38
View File
@@ -1,38 +0,0 @@
/**
* The configuration for `ng-dev commit-message` commands.
*
* @type { import("@angular/ng-dev").CommitMessageConfig }
*/
export const commitMessage = {
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',
'benchpress',
'common',
'compiler',
'compiler-cli',
'core',
'dev-infra',
'devtools',
'docs-infra',
'elements',
'forms',
'http',
'language-service',
'language-server',
'localize',
'migrations',
'platform-browser',
'platform-browser-dynamic',
'platform-server',
'router',
'service-worker',
'upgrade',
'vscode-extension',
'zone.js',
],
};
+39
View File
@@ -0,0 +1,39 @@
import {CommitMessageConfig} from '@angular/ng-dev';
/**
* The configuration for `ng-dev commit-message` commands.
*/
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',
'benchpress',
'changelog',
'common',
'compiler',
'compiler-cli',
'core',
'dev-infra',
'devtools',
'docs-infra',
'elements',
'forms',
'http',
'language-service',
'localize',
'migrations',
'packaging',
'platform-browser',
'platform-browser-dynamic',
'platform-server',
'router',
'service-worker',
'upgrade',
'zone.js',
],
};
-8
View File
@@ -1,8 +0,0 @@
import {caretaker} from './caretaker.mjs';
import {commitMessage} from './commit-message.mjs';
import {format} from './format.mjs';
import {github} from './github.mjs';
import {pullRequest} from './pull-request.mjs';
import {release} from './release.mjs';
export {commitMessage, format, github, pullRequest, caretaker, release};
+15
View File
@@ -0,0 +1,15 @@
import {caretaker} from './caretaker.mjs';
import {commitMessage} from './commit-message.mjs';
import {format} from './format.mjs';
import {github} from './github.mjs';
import {pullRequest} from './pull-request.mjs';
import {release} from './release.mjs';
export {
commitMessage,
format,
github,
pullRequest,
caretaker,
release,
};
-9
View File
@@ -1,9 +0,0 @@
/**
* Configuration for the `ng-dev format` command.
*
* @type { import("@angular/ng-dev").FormatConfig }
*/
export const format = {
'prettier': true,
'buildifier': true,
};
+32
View File
@@ -0,0 +1,32 @@
import {FormatConfig} from '@angular/ng-dev';
/**
* Configuration for the `ng-dev format` command.
*/
export const format: FormatConfig = {
'prettier': {
'matchers': [
'**/*.{yaml,yml}',
'**/*.{js,ts,mjs,mts,cjs,cts,tsx}',
'devtools/**/*.{js,ts,mjs,mts,cjs,cts,html,scss}',
'integration/**/size.json',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
// Both third_party and .yarn are directories containing copied code which should
// not be modified.
'!third_party/**',
'!.yarn/**',
// Do not format the locale files which are checked-in for Google3, but generated using
// the `generate-locales-tool` from `packages/common/locales`.
'!packages/core/src/i18n/locale_en.ts',
'!packages/common/locales/closure-locale.ts',
'!packages/common/src/i18n/currencies.ts',
// Test cases contain non valid code.
'!packages/compiler-cli/test/compliance/test_cases/**/*.{js,ts,mjs,mts,cjs,cts}',
// Ignore generated javascript file(s)
'!.github/actions/deploy-docs-site/main.js',
],
},
'buildifier': true,
};
-12
View File
@@ -1,12 +0,0 @@
/**
* Github configuration for the `ng-dev` command. This repository is used as
* remote for the merge script and other utilities like `ng-dev pr rebase`.
*
* @type { import("@angular/ng-dev").GithubConfig }
*/
export const github = {
owner: 'angular',
name: 'angular',
mainBranchName: 'main',
mergeMode: 'caretaker-only',
};
+12
View File
@@ -0,0 +1,12 @@
import {GithubConfig} from '@angular/ng-dev';
/**
* Github configuration for the `ng-dev` command. This repository is used as
* remote for the merge script and other utilities like `ng-dev pr rebase`.
*/
export const github: GithubConfig = {
owner: 'angular',
name: 'angular',
mainBranchName: 'main',
useNgDevAuthService: true,
};
-34
View File
@@ -1,34 +0,0 @@
/**
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
* are respected by the merge script (e.g. the target labels).
*
* @type { import("@angular/ng-dev").PullRequestConfig }
*/
export const pullRequest = {
githubApiMerge: {
default: 'auto',
labels: [{pattern: 'merge: squash commits', method: 'squash'}],
},
requiredBaseCommits: {
// PRs that target either `main` or the patch branch, need to be rebased
// on top of the latest commit message validation fix.
// These SHAs are the commits that update the required license text in the header.
'main': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
'10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24',
},
// `docs-infra` are not affecting the public NPM packages.
targetLabelExemptScopes: ['docs-infra'],
// enables specific validations during the pull request merge process
validators: {
assertEnforceTested: true,
assertIsolatedSeparateFiles: true,
},
requiredStatuses: [
{type: 'check', name: 'test'},
{type: 'check', name: 'lint'},
{type: 'check', name: 'adev'},
{type: 'check', name: 'zone-js'},
{type: 'status', name: 'google-internal-tests'},
],
};
+33
View File
@@ -0,0 +1,33 @@
import {PullRequestConfig} from '@angular/ng-dev';
/**
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
* are respected by the merge script (e.g. the target labels).
*/
export const pullRequest: PullRequestConfig = {
githubApiMerge: false,
requiredBaseCommits: {
// PRs that target either `main` or the patch branch, need to be rebased
// on top of the latest commit message validation fix.
// These SHAs are the commits that update the required license text in the header.
'main': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
'10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24',
},
// `dev-infra` and `docs-infra` are not affecting the public NPM packages. Similarly,
// the `bazel` package is not considered part of the public API so that features
// can land in patch branches.
targetLabelExemptScopes: ['dev-infra', 'docs-infra', 'bazel'],
// enables specific validations during the pull request merge process
validators: {
assertEnforceTested: true,
assertIsolatedSeparateFiles: true,
},
requiredStatuses: [
{type: 'check', name: 'test'},
{type: 'check', name: 'lint'},
{type: 'check', name: 'adev'},
{type: 'check', name: 'zone-js'},
{type: 'status', name: 'google-internal-tests'},
],
};
-44
View File
@@ -1,44 +0,0 @@
/**
* Configuration for the `ng-dev release` command.
*
* @type { import("@angular/ng-dev").ReleaseConfig }
*/
export const release = {
publishRegistry: 'https://wombat-dressing-room.appspot.com',
representativeNpmPackage: '@angular/core',
npmPackages: [
{name: '@angular/animations'},
{name: '@angular/common'},
{name: '@angular/compiler-cli'},
{name: '@angular/compiler'},
{name: '@angular/core'},
{name: '@angular/elements'},
{name: '@angular/forms'},
{name: '@angular/language-server'},
{name: '@angular/language-service'},
{name: '@angular/localize'},
{name: '@angular/platform-browser-dynamic'},
{name: '@angular/platform-browser'},
{name: '@angular/platform-server'},
{name: '@angular/router'},
{name: '@angular/service-worker'},
{name: '@angular/upgrade'},
],
buildPackages: async () => {
// The buildTargetPackages function is loaded at runtime as the loading the script
// causes an invocation of Bazel.
const {performNpmReleaseBuild} = await import('../scripts/build/package-builder.mjs');
return performNpmReleaseBuild();
},
releaseNotes: {
hiddenScopes: [
'dev-infra',
'docs-infra',
'zone.js',
'devtools',
'vscode-extension',
'benchpress',
],
},
releasePrLabels: ['area: build & ci', 'action: merge', 'PullApprove: disable'],
};
+35
View File
@@ -0,0 +1,35 @@
import {ReleaseConfig} from '@angular/ng-dev';
/** Configuration for the `ng-dev release` command. */
export const release: ReleaseConfig = {
rulesJsInteropMode: true,
publishRegistry: 'https://wombat-dressing-room.appspot.com',
representativeNpmPackage: '@angular/core',
npmPackages: [
{name: '@angular/animations'},
{name: '@angular/common'},
{name: '@angular/compiler'},
{name: '@angular/compiler-cli'},
{name: '@angular/core'},
{name: '@angular/elements'},
{name: '@angular/forms'},
{name: '@angular/language-service'},
{name: '@angular/localize'},
{name: '@angular/platform-browser'},
{name: '@angular/platform-browser-dynamic'},
{name: '@angular/platform-server'},
{name: '@angular/router'},
{name: '@angular/service-worker'},
{name: '@angular/upgrade'},
],
buildPackages: async () => {
// The buildTargetPackages function is loaded at runtime as the loading the script
// causes an invocation of Bazel.
const {performNpmReleaseBuild} = await import('../scripts/build/package-builder.mjs');
return performNpmReleaseBuild();
},
releaseNotes: {
hiddenScopes: ['bazel', 'dev-infra', 'docs-infra', 'zone.js', 'devtools'],
},
releasePrLabels: ['area: build & ci', 'action: merge', 'PullApprove: disable'],
};
+1 -1
View File
@@ -5,6 +5,6 @@
"module": "Node16",
"noEmit": true,
"skipLibCheck": true,
"types": ["node"]
"types": []
}
}
+1 -1
View File
@@ -1 +1 @@
22.21.1
22.18.0
+2 -2
View File
@@ -25,7 +25,7 @@ function readPackage(pkg, context) {
pkg.dependencies = {
...pkg.dependencies,
'@angular/compiler': 'workspace:*',
'typescript': '5.9.3',
'typescript': '5.9.0-beta',
};
}
@@ -42,7 +42,7 @@ function readPackage(pkg, context) {
// Change all locally generated packages to directly depend on the other local packages, instead
// of expecting them as peerDependencies automatically as we do not auto install peer deps. The
// package is also removed from peerDependencies as it was moved over and will just cause errors.
if (localAngularPackages.includes(key) && pkg.version === '0.0.0-PLACEHOLDER') {
if (localAngularPackages.includes(key)) {
pkg.dependencies = {
...pkg.dependencies,
[key]: 'workspace: *',
+2 -34
View File
@@ -1,35 +1,3 @@
pnpm-lock.yaml
.prettierrc
# Do not format d.ts files as they are generated
**/*.d.ts
# Both third_party and .yarn are directories containing copied code which should
# not be modified.
third_party/**/*.js
third_party/**/*.css
third_party/**/*.md
third_party/**/*.json
# Do not format the locale files which are checked-in for Google3, but generated using
# the `generate-locales-tool` from `packages/common/locales`.
packages/core/src/i18n/locale_en.ts
packages/common/locales/closure-locale.ts
packages/common/src/i18n/currencies.ts
# Test cases contain non valid code.
packages/compiler-cli/test/compliance/test_cases/**/*.js
packages/compiler-cli/test/compliance/test_cases/**/*.ts
# Ignore generated javascript file(s)
.github/actions/deploy-docs-site/main.js
# Ignore testing data files for language service
vscode-ng-language-service/syntaxes/test/data/*.ts
# Ignore goldens MD files
goldens/**/*.api.md
# adev generated files
adev/src/content/aria/**/*.json
adev/src/content/cli/**/*.json
adev/src/content/cdk/**/*.json
pnpm-lock.yaml
+1 -16
View File
@@ -7,20 +7,5 @@
"semi": true,
"quoteProps": "preserve",
"bracketSpacing": false,
"trailingComma": "all",
"overrides": [
{
"files": ["./.prettierrc"],
"options": {
"parser": "json"
}
},
{
"files": ["*.html"],
"excludeFiles": ["**/test/**"],
"options": {
"parser": "angular"
}
}
]
"trailingComma": "all"
}
+74 -31
View File
@@ -15,6 +15,9 @@
# - We trust that people do the right thing and won't approve changes they don't feel confident reviewing
# - We enforce that only approved PRs are merged ensuring that unreviewed code isn't accidentally merged
# - We distribute approval rights as much as possible to help us scale better
# - Groups have one or two global approvers groups as fallbacks:
# - @angular/fw-global-approvers: for approving minor changes, large-scale refactorings, and emergency situations.
# - @angular/fw-global-approvers-for-docs-only-changes: for approving minor documentation-only changes that don't require engineering review
# - A small number of file groups have very limited number of reviewers because incorrect changes to the files they guard would have serious consequences (e.g. security, public api)
#
# Configuration nuances:
@@ -34,10 +37,27 @@
# See reviewer list under `required-minimum-review` group. Team member names and
# usernames are managed there.
####################################################################################
# Approval Groups
####################################################################################
# =========================================================
# @angular/framework-global-approvers
# =========================================================
# Used for approving minor changes, large-scale refactorings, and in emergency situations.
#
# alxhub
# jelbourn
# josephperrott
#
# =========================================================
# @angular/framework-global-approvers-for-docs-only-changes
# =========================================================
# Used for approving minor documentation-only changes that don't require engineering review.
version: 3
#availability:
# users_unavailable: []
availability:
users_unavailable: ['devversion']
# Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config.
@@ -77,6 +97,14 @@ overrides:
- if: len(groups.active.exclude("required-minimum-review").exclude("global-*")) == 0 and len(groups.approved.include("global-*")) == 0
status: failure
explanation: 'At least one group must match this PR. Please update an existing review group, or create a new group.'
# If any global dev-infra approval is given the status should be passing.
- if: len(groups.approved.include("global-dev-infra-approvers")) == 1
status: success
explanation: 'Passing as globally approved by dev-infra'
# If any global docs approval is given the status should be passing.
- if: len(groups.approved.include("global-docs-approvers")) == 1
status: success
explanation: 'Passing as globally approved by docs'
# If any global approval is given the status should be passing.
- if: len(groups.approved.include("global-approvers")) == 1
status: success
@@ -107,10 +135,6 @@ groups:
- kirjs
- JoostK
- mmalerba
- ~amishne
- ~leonsenft
- ~mattrbeck
- ~JeanMeche
# =========================================================
# Framework: General (most code in our packages)
@@ -140,7 +164,6 @@ groups:
'packages/router/**/{*,.*}',
'packages/service-worker/**/{*,.*}',
'packages/upgrade/**/{*,.*}',
'vscode-ng-language-service/**/{*,.*}',
])
reviewers:
users:
@@ -155,9 +178,6 @@ groups:
- ~pkozlowski-opensource
- mmalerba
- JeanMeche
- ~amishne
- ~leonsenft
- ~mattrbeck
# =========================================================
# Framework: Security-sensitive files which require extra review
@@ -211,9 +231,6 @@ groups:
- thePunderWoman
- ~pkozlowski-opensource
- mmalerba
- ~amishne
- ~leonsenft
- ~mattrbeck
# =========================================================
# Tooling: Compiler API shared with Angular CLI
@@ -263,13 +280,10 @@ groups:
- devversion
- josephperrott
- ~pkozlowski-opensource
- ~mgechev
- mgechev
- MarkTechson
- mmalerba
- ~hawkgs
- ~amishne
- ~leonsenft
- ~mattrbeck
# =========================================================
# Angular DevTools
@@ -287,7 +301,7 @@ groups:
- ~devversion
- dgp1130
- hawkgs
- hybrist
- jkrems
- ~josephperrott
- JeanMeche
- milomg
@@ -307,6 +321,7 @@ groups:
'.gemini/**/{*,.*}',
'.ng-dev/**/{*,.*}',
'.vscode/**/{*,.*}',
'.yarn/**/{*,.*}',
'contributing-docs/*.md',
'contributing-docs/images/**/{*,.*}',
'goldens/{*,.*}',
@@ -379,16 +394,13 @@ groups:
- crisbeto
- devversion
- JeanMeche
- ~hybrist
- ~jkrems
- ~iteriani
- ~tbondwilkinson
- ~rahatarmanahmed
- ~amishne
- ~leonsenft
- ~mattrbeck
reviews:
request: 2
required: 1
request: 3 # Request reviews from 3 people
required: 2 # Require that 2 people approve
reviewed_for: required
# ================================================
@@ -413,9 +425,6 @@ groups:
- thePunderWoman
- ~pkozlowski-opensource
- mmalerba
- ~amishne
- ~leonsenft
- ~mattrbeck
reviews:
request: 2 # Request reviews from 2 people
required: 1 # Require that 1 person approve
@@ -509,10 +518,44 @@ groups:
global-approvers:
type: optional
reviewers:
users:
- alxhub
- jelbourn
- josephperrott
teams:
- framework-global-approvers
reviews:
request: 0
required: 1
reviewed_for: required
# =========================================================
# Global Approvers For Docs
#
# All reviews performed for global docs approvals require
# using the `Reviewed-for:` specifier to set the approval
# specificity as documented at:
# https://docs.pullapprove.com/reviewed-for/
# =========================================================
global-docs-approvers:
type: optional
reviewers:
teams:
- framework-global-approvers-for-docs-only-changes
reviews:
request: 0
required: 1
reviewed_for: required
# =========================================================
# Global Approvers For Dev-Infra changes
#
# All reviews performed for global dev-infra approvals
# require using the `Reviewed-for:` specifier to set the
# approval specificity as documented at:
# https://docs.pullapprove.com/reviewed-for/
# =========================================================
global-dev-infra-approvers:
type: optional
reviewers:
teams:
- dev-infra-framework
reviews:
request: 0
required: 1
+2
View File
@@ -8,6 +8,8 @@ To use the recommended configurations follow the steps below:
- install the recommended extensions in `.vscode/extensions.json`
- copy (or link) `.vscode/recommended-settings.json` to `.vscode/settings.json`
- copy (or link) `.vscode/recommended-launch.json` to `.vscode/launch.json`
- copy (or link) `.vscode/recommended-tasks.json` to `.vscode/tasks.json`
- restart the editor
If you already have your custom workspace settings, you should instead manually merge the file contents.
-84
View File
@@ -1,84 +0,0 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.2.0",
"configurations": [
{
"type": "extensionHost",
"request": "launch",
"name": "VSCE: Launch Dev Client",
"runtimeExecutable": "${execPath}",
"args": [
"--disable-extensions",
"--extensionDevelopmentPath=${workspaceFolder}/vscode-ng-language-service"
],
"preLaunchTask": "VSCE: watch bundles"
},
{
"type": "extensionHost",
"request": "launch",
"name": "VSCE: Launch Prod Client",
"runtimeExecutable": "${execPath}",
"args": [
"--disable-extensions",
"--extensionDevelopmentPath=${workspaceFolder}/dist/bin/vscode-ng-language-service/development_package"
],
"preLaunchTask": "VSCE: package"
},
{
"name": "VSCE: Attach to Server",
"type": "node",
"request": "attach",
"port": 6009,
"restart": true,
"sourceMaps": true,
"skipFiles": ["<node_internals>/**"],
"sourceMapPathOverrides": {
"?:*/bin/*": "${workspaceFolder}/*"
},
"resolveSourceMapLocations": ["!**/node_modules/**"]
},
{
"name": "DEBUG: Attach to bazel test",
"type": "node",
"request": "attach",
"port": 9229,
"restart": true,
"timeout": 600000,
"sourceMaps": true,
"skipFiles": ["<node_internals>/**"],
"sourceMapPathOverrides": {
"?:*/bin/*": "${workspaceFolder}/*"
},
"resolveSourceMapLocations": ["!**/node_modules/**"]
},
{
"name": "DEBUG: Run bazel test (Custom Target)",
"type": "node",
"request": "launch",
"restart": true,
"timeout": 600000,
"runtimeExecutable": "pnpm",
"runtimeArgs": ["bazel", "test", "${input:bazelTarget}", "--config=debug"],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}"
}
],
"compounds": [
{
"name": "VSCE: Dev Client + Attach to Server",
"configurations": ["VSCE: Launch Dev Client", "VSCE: Attach to Server"]
},
{
"name": "DEBUG: Run bazel test (Custom Target) + Attach",
"configurations": ["DEBUG: Attach to bazel test", "DEBUG: Run bazel test (Custom Target)"]
}
],
"inputs": [
{
"id": "bazelTarget",
"type": "promptString",
"description": "Enter the Bazel test target (e.g., //path/to/my:unit_test)",
"default": "//packages/..."
}
]
}
+75
View File
@@ -0,0 +1,75 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to bazel test ... --config=debug",
"type": "node",
"request": "attach",
"port": 9229,
"address": "localhost",
"restart": false,
"sourceMaps": true,
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}",
"stopOnEntry": false,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/dist/out/**/packages/**/*.js"]
},
{
"name": "Attach to bazel test ... --config=debug (no source maps)",
"type": "node",
"request": "attach",
"port": 9229,
"address": "localhost",
"restart": false,
"sourceMaps": false,
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}",
"stopOnEntry": false,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/dist/out/**/packages/**/*.js"]
},
{
"name": "IVY:packages/core/test/acceptance",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": ["test", "packages/core/test/acceptance", "--config=debug"],
"port": 9229,
"address": "localhost",
"restart": true,
"sourceMaps": true,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/dist/out/**/packages/**/*.js"]
},
{
"name": "IVY:packages/core/test/render3",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": ["test", "packages/core/test/render3", "--config=debug"],
"port": 9229,
"address": "localhost",
"restart": true,
"sourceMaps": true,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/dist/out/**/packages/**/*.js"]
},
{
"name": "IVY:packages/core/test",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": ["test", "packages/core/test", "--config=debug"],
"port": 9229,
"address": "localhost",
"restart": true,
"sourceMaps": true,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/dist/out/**/packages/**/*.js"]
}
]
}
+3 -1
View File
@@ -21,5 +21,7 @@
".history": true
},
"git.ignoreLimitWarning": true,
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file .git-blame-ignore-revs"]
"gitlens.advanced.blame.customArguments": [
"--ignore-revs-file .git-blame-ignore-revs"
]
}
+109
View File
@@ -0,0 +1,109 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "IVY:packages/core/test/...",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test",
"packages/core/test/acceptance",
"packages/core/test/render3",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "VE:packages/core/test/...",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test",
"packages/core/test/acceptance",
"packages/core/test/render3",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "IVY:packages/core/test/acceptance",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test/acceptance",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "VE:packages/core/test/acceptance",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test/acceptance",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "IVY:packages/core/test",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "VE:packages/core/test",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "IVY:packages/core/test/render3",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test/render3",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
],
}
-32
View File
@@ -1,32 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "VSCE: watch bundles",
"command": "pnpm --filter=ng-template run watch",
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": {
"base": "$tsc-watch",
"background": {
"activeOnStart": true,
"beginsPattern": "^iBazel \\[\\d{1,2}:\\d{1,2}(?:AM|PM)\\]: Querying for files to watch.*",
"endsPattern": "^INFO: Build completed successfully, \\d+ total action(s)?"
}
}
},
{
"type": "shell",
"label": "VSCE: package",
"command": "pnpm --filter=ng-template run package"
}
]
}
+13
View File
@@ -0,0 +1,13 @@
# Yarn Vendoring
We utilize Yarn's `yarn-path` configuration in a shared `.yarnrc` file to enforce
everyone using the same version of Yarn. Yarn checks the `.yarnrc` file to
determine if yarn should delegate the command to a vendored version at the
provided path.
## How to update
To update to the latest version of Yarn as our vendored version:
- Run this command
```sh
yarn policies set-version latest
```
- Remove the previous version
+148049
View File
File diff suppressed because one or more lines are too long
+5
View File
@@ -0,0 +1,5 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
yarn-path ".yarn/releases/yarn-1.22.22.cjs"
+14 -23
View File
@@ -1,8 +1,7 @@
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@devinfra//bazel/validation:defs.bzl", "validate_ts_version_matching")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("//tools:defaults.bzl", "copy_to_bin")
load("//:yarn.bzl", "YARN_PATH")
load("//tools:defaults.bzl", "copy_to_bin", "js_binary")
package(default_visibility = ["//visibility:public"])
@@ -10,7 +9,9 @@ exports_files([
"LICENSE",
"karma-js.conf.js",
"browser-providers.conf.js",
YARN_PATH,
"package.json",
"angular.json",
])
npm_link_all_packages(
@@ -24,11 +25,6 @@ filegroup(
srcs = ["package.json"],
)
validate_ts_version_matching(
module_lock_file = "MODULE.bazel.lock",
package_json = "package.json",
)
alias(
name = "tsconfig.json",
actual = "//packages:tsconfig-build.json",
@@ -64,6 +60,16 @@ config_setting(
values = {"stamp": "true"},
)
js_binary(
name = "yarn_vendored",
data = [".yarn/releases/yarn-1.22.22.cjs"],
entry_point = ".yarn/releases/yarn-1.22.22.cjs",
visibility = [
"//integration:__subpackages__",
"//modules/ssr-benchmarks:__subpackages__",
],
)
alias(
name = "sauce_connect",
actual = select({
@@ -72,18 +78,3 @@ alias(
"@devinfra//bazel/constraints:macos_arm64": "@sauce_connect_mac//:bin/sc",
}),
)
# When enabled, this flag substitutes dependency versions with snapshot repositories
# for all packages in this repository. Note that this does not apply to peer
# dependencies, as they must be installed directly.
bool_flag(
name = "enable_language_server_snapshot_repo_deps",
build_setting_default = False,
)
config_setting(
name = "language_server_package_json_use_snapshot_repo_deps",
flag_values = {
":enable_language_server_snapshot_repo_deps": "true",
},
)
+3013 -5455
View File
File diff suppressed because it is too large Load Diff
+5035 -5519
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -14,20 +14,20 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
- Use welcoming and inclusive language
- Respect each other
- Provide and gracefully accept constructive criticism
- Show empathy towards other community members
* Use welcoming and inclusive language
* Respect each other
* Provide and gracefully accept constructive criticism
* Show empathy towards other community members
Examples of unacceptable behavior by participants include:
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
- The use of sexualized language or imagery
- Unwelcome sexual attention or advances
- Other conduct which could reasonably be considered inappropriate in a
* The use of sexualized language or imagery
* Unwelcome sexual attention or advances
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
@@ -69,9 +69,9 @@ members of the project's leadership.
If you are banned you may contest the decision. To do so email conduct@angular.io with the subject line "Repeal Ban for {{your name here}}" and body with the responses to the following:
- Why do you believe you did not violate the Code of Conduct?
- Were other factors involved in this situation the leadership team may have been unaware of?
- Why do you wish to be a part of the Angular community?
* Why do you believe you did not violate the Code of Conduct?
* Were other factors involved in this situation the leadership team may have been unaware of?
* Why do you wish to be a part of the Angular community?
## Attribution
+90 -97
View File
@@ -3,20 +3,22 @@
We would love for you to contribute to Angular and help make it even better than it is today!
As a contributor, here are the guidelines we would like you to follow:
- [Code of Conduct](#coc)
- [Question or Problem?](#question)
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submit)
- [Coding Rules](#rules)
- [Commit Message Guidelines](#commit)
- [Signing the CLA](#cla)
- [Code of Conduct](#coc)
- [Question or Problem?](#question)
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submit)
- [Coding Rules](#rules)
- [Commit Message Guidelines](#commit)
- [Signing the CLA](#cla)
## <a name="coc"></a> Code of Conduct
Help us keep Angular open and inclusive.
Please read and follow our [Code of Conduct][coc].
## <a name="question"></a> Got a Question or Problem?
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests.
@@ -32,25 +34,28 @@ To save your and our time, we will systematically close all issues that are requ
If you would like to chat about the question in real-time, you can reach out via [the Angular community Discord server][discord].
## <a name="issue"></a> Found a Bug?
If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our [GitHub Repository][github].
Even better, you can [submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> Missing a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
If you would like to *implement* a new feature, please consider the size of the change in order to determine the right steps to proceed:
You can _request_ a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
If you would like to _implement_ a new feature, please consider the size of the change in order to determine the right steps to proceed:
- For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
**Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
- **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## <a name="submit"></a> Submission Guidelines
### <a name="submit-issue"></a> Submitting an Issue
Before you submit an issue, please search the issue tracker. An issue for your problem might already exist and the discussion might inform you of workarounds readily available.
@@ -69,67 +74,50 @@ Unfortunately, we are not able to investigate / fix bugs without a minimal repro
You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
### <a name="pr-quality"></a> Contribution Quality
We strongly value open source contribution and pull requests from community contributors. Please note that every pull request is reviewed and merged by an actual person on the team, which does take time and effort. That is time and effort that does take away from other valuable work. With that in mind we have an minimum set of expectations that are required of any community contribution pull request that is opened.
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
- You don't want to duplicate existing efforts.
2. Be sure that an issue or pull request clearly describes the problem you're fixing, or documents the design for the feature you'd like to add. Issues require a _minimal_ reproduction.
3. Discussing the design in an issue upfront helps to ensure that we're ready to accept your work. Pull requests are not the right place to do design work.
- When in doubt, open an issue first before doing any sort of speculative implementation work
4. Ideally the PR should be tied to an issue, but this is not required
5. The change should improve code quality (i.e. addressing a TODO) or should impact / improve a feature
6. Micro optimizations will only be accepted if they are validated by an actual benchmark
7. Do not open pull requests that are addressing feature requests that are not labeled as "help wanted" as they usually need additional design work before we could accept pull requests
8. The change should be well tested
If your pull request does not meet these minimum expectations, we may close your PR. Also, if your PR introduces a breaking change, it's possible the level of churn this breaking change causes may block our ability to move forward with it. We may close your PR in that situation, as well. Otherwise, we're excited to see your contributions and enthusiasm for Angular!
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
You don't want to duplicate existing efforts.
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
Discussing the design upfront helps to ensure that we're ready to accept your work.
3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without a signed CLA.
Make sure you author all contributed Git commits with email address associated with your CLA signature.
2. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the [angular/angular](https://github.com/angular/angular/fork) repo.
4. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the [angular/angular](https://github.com/angular/angular/fork) repo.
3. In your forked repository, make your changes in a new git branch:
5. In your forked repository, make your changes in a new git branch:
```shell
git checkout -b my-fix-branch main
```
```shell
git checkout -b my-fix-branch main
```
4. Create your patch, **including appropriate test cases**.
6. Create your patch, **including appropriate test cases**.
5. Follow our [Coding Rules](#rules).
7. Follow our [Coding Rules](#rules).
6. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
7. Commit your changes using a descriptive commit message that follows our [commit message conventions][commit-message-guidelines].
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
git commit --all
```
```shell
git commit --all
```
Note: the optional commit `--all` command line option will automatically "add" and "rm" edited files.
Note: the optional commit `--all` command line option will automatically "add" and "rm" edited files.
10. Push your branch to GitHub:
8. Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
```shell
git push origin my-fix-branch
```
9. In GitHub, send a pull request to `angular:main`.
11. In GitHub, send a pull request to `angular:main`.
### Reviewing a Pull Request
@@ -145,15 +133,16 @@ If we ask for changes via code reviews then:
3. Create a fixup commit and push to your GitHub repository (this will update your Pull Request):
```shell
git commit --all --fixup HEAD
git push
```
```shell
git commit --all --fixup HEAD
git push
```
For more info on working with fixup commits see [here](./contributing-docs/using-fixup-commits.md).
For more info on working with fixup commits see [here](./contributing-docs/using-fixup-commits.md).
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-message-guidelines]).
@@ -161,68 +150,69 @@ In order to update the commit message of the last commit on your branch:
1. Check out your branch:
```shell
git checkout my-fix-branch
```
```shell
git checkout my-fix-branch
```
2. Amend the last commit and modify the commit message:
```shell
git commit --amend
```
```shell
git commit --amend
```
3. Push to your GitHub repository:
```shell
git push --force-with-lease
```
```shell
git push --force-with-lease
```
> NOTE:<br />
> If you need to update the commit message of an earlier commit, you can use `git rebase` in interactive mode.
> See the [git docs](https://git-scm.com/docs/git-rebase#_interactive_mode) for more details.
#### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
- Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell
git push origin --delete my-fix-branch
```
```shell
git push origin --delete my-fix-branch
```
- Check out the main branch:
* Check out the main branch:
```shell
git checkout main -f
```
```shell
git checkout main -f
```
- Delete the local branch:
* Delete the local branch:
```shell
git branch -D my-fix-branch
```
```shell
git branch -D my-fix-branch
```
- Update your local `main` with the latest upstream version:
* Update your local `main` with the latest upstream version:
```shell
git pull --ff upstream main
```
```shell
git pull --ff upstream main
```
## <a name="rules"></a> Coding Rules
To ensure consistency throughout the source code, keep these rules in mind as you are working:
- All features or bug fixes **must be tested** by one or more specs (unit-tests).
- All public API methods **must be documented**.
- We follow [Google's TypeScript Style Guide][ts-style-guide], but wrap all code at **100 characters**.
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
* All public API methods **must be documented**.
* We follow [Google's TypeScript Style Guide][ts-style-guide], but wrap all code at **100 characters**.
An automated formatter is available, see [building-and-testing-angular.md](./contributing-docs/building-and-testing-angular.md#formatting-your-source-code).
An automated formatter is available, see [building-and-testing-angular.md](./contributing-docs/building-and-testing-angular.md#formatting-your-source-code).
## <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>
```
@@ -234,18 +224,21 @@ See [Commit Message Guidelines][commit-message-guidelines] for details.
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
- For individuals, we have a [simple click-through form][individual-cla].
- For corporations, we'll need you to
* For individuals, we have a [simple click-through form][individual-cla].
* For corporations, we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
- https://help.github.com/articles/setting-your-commit-email-address-in-git/
- https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
- https://help.github.com/articles/about-commit-email-addresses/
- https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
* https://help.github.com/articles/about-commit-email-addresses/
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
[coc]: https://github.com/angular/code-of-conduct/blob/main/CODE_OF_CONDUCT.md
[corporate-cla]: https://cla.developers.google.com/about/google-corporate
+24 -33
View File
@@ -5,41 +5,41 @@ module(
)
bazel_dep(name = "rules_pkg", version = "1.1.0")
bazel_dep(name = "rules_nodejs", version = "6.6.2")
bazel_dep(name = "aspect_rules_ts", version = "3.7.1")
bazel_dep(name = "aspect_rules_js", version = "2.8.3")
bazel_dep(name = "aspect_rules_esbuild", version = "0.24.0")
bazel_dep(name = "rules_nodejs", version = "6.5.0")
bazel_dep(name = "aspect_rules_ts", version = "3.7.0")
bazel_dep(name = "aspect_rules_js", version = "2.5.0")
bazel_dep(name = "aspect_rules_esbuild", version = "0.22.1")
bazel_dep(name = "aspect_rules_jasmine", version = "2.0.0")
bazel_dep(name = "aspect_rules_rollup", version = "2.0.1")
bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "aspect_bazel_lib", version = "2.22.0")
bazel_dep(name = "tar.bzl", version = "0.7.0")
bazel_dep(name = "yq.bzl", version = "0.3.2")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "aspect_bazel_lib", version = "2.21.1")
bazel_dep(name = "tar.bzl", version = "0.5.5")
bazel_dep(name = "yq.bzl", version = "0.2.0")
bazel_dep(name = "rules_angular")
git_override(
module_name = "rules_angular",
commit = "7133b97252508f8528e5c5818a9a73cacc2e2a0e",
commit = "17eac47ea99057f7473a7d93292e76327c894ed9",
remote = "https://github.com/devversion/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
commit = "90bba8b673b749ce67760b4d993b9b421adac30f",
commit = "00a4cf14cdc374867673a52ecb556f356da8bec0",
remote = "https://github.com/angular/dev-infra.git",
)
bazel_dep(name = "rules_sass")
git_override(
module_name = "rules_sass",
commit = "1184a80751a21af8348f308abc5b38a41f26850e",
commit = "76078d5e9776a0080dcee496e90b88d8a6179c19",
remote = "https://github.com/devversion/rules_sass.git",
)
bazel_dep(name = "rules_browsers")
git_override(
module_name = "rules_browsers",
commit = "8ef3e996d5fc040a35770f860987d8b9cad8ef3d",
commit = "8ee9ae3216ef26516c8ef20537c89857343cdc3a",
remote = "https://github.com/devversion/rules_browsers.git",
)
@@ -47,7 +47,7 @@ yq = use_extension("@yq.bzl//yq:extensions.bzl", "yq")
use_repo(yq, "yq_toolchains")
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
node.toolchain(node_version = "22.21.1")
node.toolchain(node_version = "20.19.0")
use_repo(node, "nodejs_toolchains")
use_repo(node, "nodejs_darwin_amd64")
use_repo(node, "nodejs_darwin_arm64")
@@ -58,10 +58,6 @@ use_repo(node, "nodejs_linux_s390x")
use_repo(node, "nodejs_windows_amd64")
pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
pnpm.pnpm(
name = "pnpm",
pnpm_version = "10.16.1",
)
use_repo(pnpm, "pnpm")
npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm")
@@ -96,8 +92,6 @@ npm.npm_translate_lock(
"//packages/service-worker:package.json",
"//packages/upgrade:package.json",
"//tools/bazel/rules_angular_store:package.json",
"//vscode-ng-language-service/integration/pre_standalone_project:package.json",
"//vscode-ng-language-service/integration/project:package.json",
],
npmrc = "//:.npmrc",
pnpm_lock = "//:pnpm-lock.yaml",
@@ -108,22 +102,19 @@ use_repo(npm, "npm")
rules_ts_ext = use_extension("@aspect_rules_ts//ts:extensions.bzl", "ext")
rules_ts_ext.deps(
name = "angular_npm_typescript",
# Obtained by: curl --silent https://registry.npmjs.org/typescript/5.9.3 | jq -r '.dist.integrity'
ts_integrity = "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
ts_version = "5.9.3",
# Obtained by: curl --silent https://registry.npmjs.org/typescript/5.9.2 | jq -r '.dist.integrity'
ts_integrity = "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
ts_version_from = "//:package.json",
)
use_repo(rules_ts_ext, **{"npm_typescript": "angular_npm_typescript"})
# TODO: Figure out how to make ng_project update whenever the packages/core::pkg target changes.
rules_angular = use_extension("@rules_angular//setup:extensions.bzl", "rules_angular")
use_repo_rule("@rules_angular//setup:repositories.bzl", "configurable_deps_repo")(
name = "rules_angular_configurable_deps",
angular_compiler_cli = "@angular//:node_modules/@angular/compiler-cli",
typescript = "@angular//:node_modules/typescript",
rules_angular.setup(
name = "components_rules_angular_configurable_deps",
angular_compiler_cli = "//:node_modules/@angular/compiler-cli",
typescript = "//:node_modules/typescript",
)
override_repo(rules_angular, "rules_angular_configurable_deps")
use_repo(rules_angular, **{"rules_angular_configurable_deps": "components_rules_angular_configurable_deps"})
register_toolchains(
"@devinfra//bazel/git-toolchain:git_linux_toolchain",
@@ -132,14 +123,14 @@ register_toolchains(
"@devinfra//bazel/git-toolchain:git_windows_toolchain",
)
CLDR_VERSION = "47"
CLDR_VERSION = "41"
cldr_json_data = use_repo_rule("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_json_data_repository")
cldr_json_data(
name = "cldr_json_data",
urls = {
"https://github.com/unicode-org/cldr-json/releases/download/%s.0.0/cldr-%s.0.0-json-full.zip" % (CLDR_VERSION, CLDR_VERSION): "bbb9a9aac2dfc534bd18288678a5984023d11d22f712f3c33425f3214bd1def6",
"https://github.com/unicode-org/cldr-json/releases/download/%s.0.0/cldr-%s.0.0-json-full.zip" % (CLDR_VERSION, CLDR_VERSION): "649b76647269e32b1b0a5f7b6eed52e9e63a1581f1afdcf4f6771e49c9713614",
},
)
@@ -148,7 +139,7 @@ cldr_xml_data = use_repo_rule("//packages/common/locales/generate-locales-tool:c
cldr_xml_data(
name = "cldr_xml_data",
urls = {
"https://github.com/unicode-org/cldr/releases/download/release-%s/core.zip" % CLDR_VERSION: "d5ee2abac64158c04884a722f8ef4830ea22b6c74aac20185be2838db8eda788",
"https://github.com/unicode-org/cldr/releases/download/release-%s/cldr-common-%s.0.zip" % (CLDR_VERSION, CLDR_VERSION): "823c6170c41e2de2c229574e8a436332d25f1c9723409867fe721e00bc92d853",
},
)
+849 -597
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -146,11 +146,11 @@ Join the conversation and help the community.
[angularelements]: https://angular.dev/guide/elements
[ssr]: https://angular.dev/guide/ssr
[schematics]: https://angular.dev/tools/cli/schematics
[lazyloading]: https://angular.dev/guide/routing/define-routes#lazily-loaded-components
[lazyloading]: https://angular.dev/guide/ngmodules/lazy-loading
[node.js]: https://nodejs.org/
[npm]: https://www.npmjs.com/get-npm
[codeofconduct]: CODE_OF_CONDUCT.md
[X (formerly Twitter)]: https://www.x.com/angular
[X (formerly Twitter)]: https://www.twitter.com/angular
[bluesky]: https://bsky.app/profile/angular.dev
[discord]: https://discord.gg/angular
[stackoverflow]: https://stackoverflow.com/questions/tagged/angular
+97 -102
View File
@@ -1,3 +1,4 @@
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("@rules_angular//src/architect:ng_application.bzl", "ng_application")
load("@rules_angular//src/architect:ng_config.bzl", "ng_config")
@@ -14,8 +15,8 @@ exports_files([
])
APPLICATION_FILES = [
"//adev/src/context:llms_full",
"//adev/src/assets/images",
"//adev/src/assets/images/v21-event:v21-event-images",
"//adev/src/assets/others",
"//adev/src/assets/previews",
"//adev/src/assets:tutorials",
@@ -24,129 +25,122 @@ APPLICATION_FILES = [
"//adev/src/assets:content",
"//adev/src/assets:context",
"//adev/src/content/examples:embeddable",
"tailwind.config.js",
] + glob(
["src/**/*"],
exclude = ["src/**/*.spec.ts"],
)
APPLICATION_DEPS = [
":node_modules/@codemirror/autocomplete",
":node_modules/@codemirror/commands",
":node_modules/@codemirror/lang-angular",
":node_modules/@codemirror/lang-css",
":node_modules/@codemirror/lang-html",
":node_modules/@codemirror/lang-javascript",
":node_modules/@codemirror/lang-sass",
":node_modules/@codemirror/language",
":node_modules/@codemirror/lint",
":node_modules/@codemirror/search",
":node_modules/@codemirror/state",
":node_modules/@codemirror/view",
":node_modules/@lezer/common",
":node_modules/@lezer/highlight",
":node_modules/@lezer/javascript",
":node_modules/@stackblitz/sdk",
":node_modules/@typescript/vfs",
":node_modules/@webcontainer/api",
":node_modules/@xterm/addon-fit",
":node_modules/@xterm/xterm",
":node_modules/algoliasearch",
":node_modules/angular-split",
":node_modules/fflate",
":node_modules/marked",
":node_modules/ngx-progressbar",
":node_modules/open-in-idx",
":node_modules/tailwindcss",
":node_modules/typescript",
":node_modules/@types/dom-navigation",
"//adev:node_modules/@angular-devkit/build-angular",
"//adev:node_modules/@codemirror/autocomplete",
"//adev:node_modules/@codemirror/commands",
"//adev:node_modules/@codemirror/lang-angular",
"//adev:node_modules/@codemirror/lang-css",
"//adev:node_modules/@codemirror/lang-html",
"//adev:node_modules/@codemirror/lang-javascript",
"//adev:node_modules/@codemirror/lang-sass",
"//adev:node_modules/@codemirror/language",
"//adev:node_modules/@codemirror/lint",
"//adev:node_modules/@codemirror/search",
"//adev:node_modules/@codemirror/state",
"//adev:node_modules/@codemirror/view",
"//adev:node_modules/@lezer/common",
"//adev:node_modules/@lezer/highlight",
"//adev:node_modules/@lezer/javascript",
"//adev:node_modules/@stackblitz/sdk",
"//adev:node_modules/@typescript/vfs",
"//adev:node_modules/@webcontainer/api",
"//adev:node_modules/@xterm/addon-fit",
"//adev:node_modules/@xterm/xterm",
"//adev:node_modules/algoliasearch",
"//adev:node_modules/angular-split",
"//adev:node_modules/fflate",
"//adev:node_modules/marked",
"//adev:node_modules/ngx-progressbar",
"//adev:node_modules/open-in-idx",
"//adev:node_modules/typescript",
"//adev:node_modules/xhr2",
"//adev:node_modules/@types/node",
"//adev:node_modules/@types/dom-navigation",
# The imports below are packages that our code does not directly import, but our upstream deps import from
# these locations directly. By design, rather than hoisting these packages into the "root" node_modules the
# dependency is found in (i.e. ./adev/node_modules), rules_js places all of each package's dependencies in
# a "hoisted" location within the `.aspect_rules_js/` directories in the node_modules directory. This prevents
# the bundler from being able to find it as expected. We instead specifically provide it as a dep within bazel
# as a mechanism of "hoisting" the package for our usage by this target.
":node_modules/@algolia/client-common",
":node_modules/@algolia/requester-browser-xhr",
":node_modules/@algolia/requester-node-http",
":node_modules/@lezer/css",
":node_modules/@lezer/html",
":node_modules/@lezer/lr",
":node_modules/@lezer/sass",
":node_modules/@marijn/find-cluster-break",
":node_modules/crelt",
":node_modules/style-mod",
":node_modules/w3c-keyname",
# Deep imports
"//adev:node_modules/@algolia/client-common",
"//adev:node_modules/@algolia/requester-browser-xhr",
"//adev:node_modules/@algolia/requester-node-http",
"//adev:node_modules/@lezer/css",
"//adev:node_modules/@lezer/html",
"//adev:node_modules/@lezer/lr",
"//adev:node_modules/@lezer/sass",
"//adev:node_modules/@marijn/find-cluster-break",
"//adev:node_modules/crelt",
"//adev:node_modules/style-mod",
"//adev:node_modules/w3c-keyname",
]
TEST_FILES = APPLICATION_FILES + glob(["src/app/**/*.spec.ts"])
TEST_FILES = APPLICATION_FILES + [
"karma.conf.js",
"test-main.ts",
] + glob(["src/**/*.spec.ts"])
TEST_DEPS = APPLICATION_DEPS + [
":node_modules/@types/jasmine",
":node_modules/karma",
":node_modules/karma-chrome-launcher",
":node_modules/karma-jasmine",
":node_modules/karma-coverage",
":node_modules/karma-jasmine-html-reporter",
":node_modules/jasmine-core",
TEST_DEPS = [
dep
for dep in APPLICATION_DEPS
if dep != "//adev:node_modules/@types/node"
] + [
"@rules_browsers//browsers/chromium",
"@rules_browsers//browsers/firefox",
"//adev/tools:windows-chromium-path",
]
ng_config(name = "ng_config")
ng_application(
name = "build",
srcs = APPLICATION_FILES + APPLICATION_DEPS,
args = [
bool_flag(
name = "full_build_adev",
build_setting_default = False,
)
config_setting(
name = "prod_build",
flag_values = {
":full_build_adev": "true",
},
)
config_setting(
name = "dev_build",
flag_values = {
":full_build_adev": "false",
},
)
config_based_architect_env = select({
":dev_build": {
"NG_BUILD_PARTIAL_SSR": "1",
},
":prod_build": {
"NG_BUILD_OPTIMIZE_CHUNKS": "1",
},
})
config_based_architect_flags = select({
":dev_build": [
"--configuration",
"development",
],
env = {
"NG_BUILD_PARTIAL_SSR": "1",
"NG_FORCE_TTY": "0",
},
ng_config = ":ng_config",
node_modules = ":node_modules",
project_name = "angular-dev",
serve_args = [
"--port",
"4201",
],
tags = [
# Tagged as manual as both build and build.production use the same output directory.
"manual",
# RBE significantly slows down the build because the CLI executes multiple CPU-intensive instances of esbuild.
# Furthermore, the current RBE machine pool lacks high-spec machines.
"no-remote-exec",
],
)
ng_application(
name = "build.production",
srcs = APPLICATION_FILES + APPLICATION_DEPS,
args = [
":prod_build": [
"--configuration",
"production",
],
env = {
"NG_BUILD_OPTIMIZE_CHUNKS": "1",
"NG_FORCE_TTY": "0",
},
})
ng_application(
name = "build",
srcs = APPLICATION_FILES + APPLICATION_DEPS,
args = config_based_architect_flags,
env = config_based_architect_env,
ng_config = ":ng_config",
node_modules = ":node_modules",
node_modules = "//adev:node_modules",
project_name = "angular-dev",
serve_args = [
"--port",
"4201",
],
tags = [
# Tagged as manual as both build and build.production use the same output directory.
"manual",
# RBE significantly slows down the build because the CLI executes multiple CPU-intensive instances of esbuild.
# Furthermore, the current RBE machine pool lacks high-spec machines.
"no-remote-exec",
],
)
ng_test(
@@ -158,9 +152,10 @@ ng_test(
"CHROMEDRIVER_BIN": "../" + "$(CHROMEDRIVER)",
},
ng_config = ":ng_config",
node_modules = ":node_modules",
node_modules = "//adev:node_modules",
project_name = "angular-dev",
toolchains = [
"@rules_browsers//browsers/chromium:toolchain_alias",
"@rules_browsers//browsers/firefox:toolchain_alias",
],
)
+1 -11
View File
@@ -17,7 +17,7 @@ git clone https://github.com/angular/angular.git
cd angular
# Install dependencies
pnpm install
pnpm
# Build and run local dev server
# NOTE: Initial build will take some time
@@ -51,13 +51,3 @@ pnpm bazel clean
# If that doesn't work, try it with the expunge flag
pnpm bazel clean --expunge
```
## High level architecture
The angular.dev website is a modern Angular application that utilizes Static Site Generation (SSG) to deliver pre-rendered, high-performance content to users.
The architecture is designed for efficiency and maintainability:
Content Sources: The documentation is sourced from two main places within the monorepo. The guides and tutorials are written in Markdown files, while the API reference pages are automatically generated by extracting documentation directly from the TypeScript source code of the Angular framework packages.
Build Process: During the build process, the Markdown files are converted into HTML. Simultaneously, the API documentation is extracted from the code comments. This content is then integrated into the Angular application.
+21 -11
View File
@@ -19,7 +19,7 @@
"build": {
"builder": "@angular/build:application",
"options": {
"externalDependencies": ["path", "xhr2"],
"externalDependencies": ["path"],
"outputMode": "static",
"outputPath": "dist",
"index": "src/index.html",
@@ -31,9 +31,9 @@
"assets": [
"src/favicon.ico",
"src/robots.txt",
"src/llms.txt",
"src/assets",
"src/assets/images/v21-event"
"src/context",
"src/llms.txt"
],
"styles": ["@angular/docs/styles/global-styles.scss", "./src/local-styles.scss"],
"scripts": [],
@@ -57,7 +57,7 @@
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "angular-dev:build:production"
@@ -75,14 +75,24 @@
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "angular-dev:build"
}
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"runner": "karma",
"browsers": ["ChromeHeadlessNoSandbox"],
"include": [
"src/app/**/*.spec.ts"
]
"builder": "@angular/build:karma",
"options": {
"tsConfig": "tsconfig.spec.json",
"include": ["src/app"],
"karmaConfig": "karma.conf.js",
"main": "test-main.ts",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["@angular/docs/styles/global-styles.scss"],
"scripts": [],
"webWorkerTsConfig": "tsconfig.worker.json"
}
}
}
+2 -14
View File
@@ -19,20 +19,7 @@
]
},
{
"source": "assets/others/versions.json",
"headers": [
{
"key": "Cross-Origin-Resource-Policy",
"value": "cross-origin"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
},
{
"source": "/**/*-[0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_].*",
"source": "/**/*-[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z].*",
"headers": [
{
"key": "Cache-Control",
@@ -69,6 +56,7 @@
"key": "Cross-Origin-Opener-Policy",
"value": "same-origin"
},
{
"key": "Cross-Origin-Embedder-Policy",
"value": "require-corp"
+85
View File
@@ -0,0 +1,85 @@
/**
* @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
*/
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const {getAdjustedChromeBinPathForWindows} = require('./tools/windows-chromium-path');
process.env.CHROME_BIN = getAdjustedChromeBinPathForWindows();
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
{'reporter:jasmine-seed': ['type', JasmineSeedReporter]},
],
proxies: {
'/dummy/image': 'src/assets/images/logos/angular/angular.png',
},
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
random: true,
seed: '',
},
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/site'),
subdir: '.',
reporters: [{type: 'html'}, {type: 'text-summary'}],
},
reporters: ['progress', 'kjhtml', 'jasmine-seed'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
// See /integration/README.md#browser-tests for more info on these args
flags: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--disable-dev-shm-usage',
'--hide-scrollbars',
'--mute-audio',
],
},
},
browsers: ['ChromeHeadlessNoSandbox'],
browserNoActivityTimeout: 60000,
singleRun: false,
restartOnFileChange: true,
});
};
// Helpers
function JasmineSeedReporter(baseReporterDecorator) {
baseReporterDecorator(this);
this.onBrowserComplete = (browser, result) => {
const seed = result.order && result.order.random && result.order.seed;
if (seed) this.write(`${browser}: Randomized with seed ${seed}.\n`);
};
this.onRunComplete = () => undefined;
}
+45 -49
View File
@@ -1,86 +1,82 @@
{
"dependencies": {
"@algolia/client-common": "5.41.0",
"@algolia/client-search": "5.41.0",
"@algolia/requester-browser-xhr": "5.41.0",
"@algolia/requester-node-http": "5.41.0",
"@angular-devkit/build-angular": "21.0.0-next.0",
"@angular/animations": "workspace:*",
"@angular/aria": "21.0.3",
"@angular/build": "21.0.3",
"@angular/cdk": "21.0.3",
"@angular/cli": "21.0.3",
"@angular/build": "21.0.0-next.0",
"@angular/cdk": "21.0.0-next.0",
"@angular/cli": "21.0.0-next.0",
"@angular/common": "workspace:*",
"@angular/compiler": "workspace:*",
"@angular/compiler-cli": "workspace:*",
"@angular/compiler": "workspace:*",
"@angular/core": "workspace:*",
"@angular/docs": "workspace:*",
"@angular/forms": "workspace:*",
"@angular/material": "21.0.3",
"@angular/material": "21.0.0-next.0",
"@angular/platform-browser": "workspace:*",
"@angular/platform-server": "workspace:*",
"@angular/router": "workspace:*",
"@angular/ssr": "21.0.3",
"@codemirror/autocomplete": "6.19.1",
"@codemirror/commands": "6.10.0",
"@angular/ssr": "21.0.0-next.0",
"@codemirror/autocomplete": "6.18.6",
"@codemirror/commands": "6.8.1",
"@codemirror/lang-angular": "0.1.4",
"@codemirror/lang-css": "6.3.1",
"@codemirror/lang-html": "6.4.11",
"@codemirror/lang-html": "6.4.9",
"@codemirror/lang-javascript": "6.2.4",
"@codemirror/lang-sass": "6.0.2",
"@codemirror/language": "6.11.3",
"@codemirror/lint": "6.9.1",
"@codemirror/lint": "6.8.5",
"@codemirror/search": "6.5.11",
"@codemirror/state": "6.5.2",
"@codemirror/view": "6.38.6",
"@lezer/common": "1.3.0",
"@lezer/css": "1.3.0",
"@lezer/highlight": "1.2.2",
"@lezer/html": "1.3.12",
"@lezer/javascript": "1.5.4",
"@lezer/lr": "1.4.2",
"@lezer/sass": "1.1.0",
"@marijn/find-cluster-break": "1.0.2",
"@codemirror/view": "6.38.1",
"@lezer/common": "1.2.3",
"@lezer/highlight": "1.2.1",
"@lezer/javascript": "1.5.1",
"@stackblitz/sdk": "1.11.0",
"@types/dom-navigation": "1.0.6",
"@types/jasmine": "5.1.12",
"@types/jsdom": "27.0.0",
"@types/node": "24.9.1",
"@typescript/vfs": "1.6.2",
"@types/jasmine": "5.1.9",
"@types/jsdom": "21.1.7",
"@types/node": "24.3.0",
"@typescript/vfs": "1.6.1",
"@webcontainer/api": "1.6.1",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0",
"algoliasearch": "5.41.0",
"algoliasearch": "5.35.0",
"angular-split": "20.0.0",
"crelt": "1.0.6",
"diff": "8.0.2",
"emoji-regex": "10.6.0",
"emoji-regex": "10.4.0",
"fflate": "0.8.2",
"jasmine-core": "5.12.0",
"jsdom": "27.0.1",
"html-entities": "2.6.0",
"jasmine-core": "5.9.0",
"jsdom": "26.1.0",
"karma-chrome-launcher": "3.2.0",
"karma-coverage": "2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0",
"marked": "16.4.1",
"mermaid": "11.12.0",
"karma-jasmine": "5.1.0",
"marked": "16.2.0",
"mermaid": "11.10.1",
"ngx-progressbar": "14.0.0",
"open-in-idx": "0.1.1",
"playwright-core": "1.56.1",
"preact": "10.27.2",
"preact-render-to-string": "6.6.3",
"playwright-core": "1.55.0",
"preact-render-to-string": "6.6.1",
"preact": "10.27.1",
"prettier": "3.6.2",
"rxjs": "7.8.2",
"shiki": "3.14.0",
"style-mod": "4.1.3",
"tinyglobby": "0.2.15",
"shiki": "3.11.0",
"tinyglobby": "0.2.14",
"tslib": "2.8.1",
"typescript": "5.9.3",
"typescript": "5.9.2",
"xhr2": "0.2.1",
"zone.js": "0.15.1",
"@algolia/client-common": "5.35.0",
"@algolia/requester-browser-xhr": "5.35.0",
"@algolia/requester-node-http": "5.35.0",
"@lezer/css": "1.3.0",
"@lezer/html": "1.3.10",
"@lezer/lr": "1.4.2",
"@lezer/sass": "1.1.0",
"@marijn/find-cluster-break": "1.0.2",
"crelt": "1.0.6",
"style-mod": "4.1.2",
"w3c-keyname": "2.2.8"
},
"devDependencies": {
"autoprefixer": "10.4.21",
"karma": "~6.4.4",
"postcss": "8.5.6",
"tailwindcss": "3.4.18"
}
}
+90
View File
@@ -0,0 +1,90 @@
/**
* @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
*/
//tslint:disable:no-console
import {execSync} from 'node:child_process';
import {existsSync, constants as fsConstants} from 'node:fs';
import {copyFile, mkdtemp, readdir, readFile, realpath, unlink, writeFile} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
export async function copyJsonAssets({repo, githubApi, assetsPath, destPath}) {
const buildInfoPath = join(destPath, '_build-info.json');
if (!existsSync(buildInfoPath)) {
throw new Error(`${buildInfoPath} does not exist.`);
}
const branch = process.env.GITHUB_REF;
const {sha: currentSha} = JSON.parse(await readFile(buildInfoPath, 'utf-8'));
const latestSha = await githubApi.getShaForBranch(branch);
console.log(`Comparing ${currentSha}...${latestSha}.`);
const affectedFiles = await githubApi.getAffectedFiles(currentSha, latestSha);
const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`));
if (changedFiles.length === 0) {
console.log(`No '${assetsPath}/**' files changed between ${currentSha} and ${latestSha}.`);
return;
}
console.log(
`The below files changed between ${currentSha} and ${latestSha}:\n` +
changedFiles.map((f) => '* ' + f).join('\n'),
);
const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'copy-json-assets-')));
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
execSync('git init', execOptions);
execSync(`git remote add origin https://github.com/${repo}.git`, execOptions);
// fetch a commit
execSync(`git fetch origin ${latestSha}`, execOptions);
// reset this repository's main branch to the commit of interest
execSync('git reset --hard FETCH_HEAD', execOptions);
// get sha when files where changed
const shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, {
encoding: 'utf8',
cwd: temporaryDir,
stdio: ['ignore', 'pipe', 'ignore'],
}).trim();
// Delete existing asset files.
const apiFilesUnlink = (await readdir(destPath))
.filter((f) => f.endsWith('.json'))
.map((f) => unlink(join(destPath, f)));
await Promise.allSettled(apiFilesUnlink);
// Copy new asset files
const tempAssetsDir = join(temporaryDir, assetsPath);
const assetFilesCopy = (await readdir(tempAssetsDir)).map((f) => {
const src = join(tempAssetsDir, f);
const dest = join(destPath, f);
return copyFile(src, dest, fsConstants.COPYFILE_FICLONE);
});
await Promise.allSettled(assetFilesCopy);
// Write SHA to file.
await writeFile(
buildInfoPath,
JSON.stringify(
{
branchName: branch,
sha: shaWhenFilesChanged,
},
undefined,
2,
),
);
console.log('\nChanges: ');
execSync(`git status --porcelain`, {stdio: 'inherit'});
console.log(`Successfully updated asset files in '${destPath}'.\n`);
}
@@ -1,25 +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
*/
import {SynonymHit} from 'algoliasearch';
/**
* List of synonyms to upload to algolia.
*
* More information about synonyms can be found here:
* https://www.algolia.com/doc/guides/managing-results/optimize-search-results/adding-synonyms/
*/
const synonyms: SynonymHit[] = [
{
objectID: 'di-synonyms',
type: 'synonym',
synonyms: ['DI', 'dependency injection'],
},
];
export default synonyms;
-58
View File
@@ -1,58 +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
*/
import {algoliasearch} from 'algoliasearch';
import synonyms from './algolia-synonyms.mjs';
/** The name of the index being updated. */
const INDEX_NAME = 'angular_v17';
/** The api key for making the change on the algolia index. */
const ALGOLIA_KEY = process.env['ALGOLIA_KEY'];
/** The algolia application id. */
const APP_NAME = 'L1XWT2UJ7F';
async function main() {
try {
if (ALGOLIA_KEY === undefined) {
throw new Error('Missing required environment variable: `ALGOLIA_KEY`');
}
const client = algoliasearch(APP_NAME, ALGOLIA_KEY);
// Obtain and list all of the previous synonyms for record keeping.
const {hits: oldHits, nbHits} = await client.searchSynonyms({indexName: INDEX_NAME});
// The list of previous synonyms cleaned to only include the information we store locally.
const oldSynonyms = oldHits.map(({objectID, type, synonyms}) => ({objectID, type, synonyms}));
// Delete all of the current synonyms
await client.clearSynonyms({indexName: INDEX_NAME, forwardToReplicas: true});
console.info(`Deleted ${nbHits} synonym(s)`);
// Upload our new synonyms
await client.saveSynonyms({
indexName: INDEX_NAME,
replaceExistingSynonyms: true,
forwardToReplicas: true,
synonymHit: synonyms,
});
console.info(`Uploaded ${synonyms.length} new synonym(s)`);
// List both the old and new synonyms for easier comparison in debugging.
console.info(`Previous Synonyms:`);
console.info(JSON.stringify(oldSynonyms, undefined, 2));
console.info(`New Synonyms:`);
console.info(JSON.stringify(synonyms, undefined, 2));
console.info('\nSuccessfully updated synonyms');
} catch (error) {
console.error('\nError updating synonyms:');
console.error(error);
process.exit(1);
}
}
main();
+3
View File
@@ -0,0 +1,3 @@
# Generating data for `angular.dev/api` CDK packages
This script updates the Angular CDK api JSON files stored in `adev/src/content/cdk`. This files are used to generate the [angular.dev api](https://angular.dev/api) pages for the CDK packages.
+35
View File
@@ -0,0 +1,35 @@
/**
* @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 {dirname, resolve as resolvePath} from 'node:path';
import {fileURLToPath} from 'node:url';
import {copyJsonAssets} from '../shared/copy-json-assets.mjs';
import {GithubClient} from '../shared/github-client.mjs';
const scriptDir = dirname(fileURLToPath(import.meta.url));
const CDK_BUILDS_REPO = 'angular/cdk-builds';
const CDK_APIS_CONTENT_PATH = resolvePath(scriptDir, '../../src/content/cdk');
async function main() {
await copyJsonAssets({
repo: CDK_BUILDS_REPO,
assetsPath: '_adev_assets',
destPath: CDK_APIS_CONTENT_PATH,
githubApi: new GithubClient(
CDK_BUILDS_REPO,
process.env.ANGULAR_CDK_BUILDS_READONLY_GITHUB_TOKEN,
'ADEV_Angular_CDK_Sources_Update',
),
});
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
+3
View File
@@ -0,0 +1,3 @@
# Generating data for `angular.dev/cli`
This script updates the Angular CLI help JSON files stored in `adev/src/content/cli/help`. This files are used to generate the [angular.dev CLI](https://angular.dev/cli) pages.
+35
View File
@@ -0,0 +1,35 @@
/**
* @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 {dirname, resolve as resolvePath} from 'node:path';
import {fileURLToPath} from 'node:url';
import {copyJsonAssets} from '../shared/copy-json-assets.mjs';
import {GithubClient} from '../shared/github-client.mjs';
const scriptDir = dirname(fileURLToPath(import.meta.url));
const CLI_BUILDS_REPO = 'angular/cli-builds';
const CLI_HELP_CONTENT_PATH = resolvePath(scriptDir, '../../src/content/cli/help');
async function main() {
await copyJsonAssets({
repo: CLI_BUILDS_REPO,
assetsPath: 'help',
destPath: CLI_HELP_CONTENT_PATH,
githubApi: new GithubClient(
CLI_BUILDS_REPO,
process.env.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN,
'ADEV_Angular_CLI_Sources_Update',
),
});
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
@@ -1,15 +0,0 @@
# Update cross-repository documentation assets
This script updates documentation assets from other repositories for `angular.dev`.
## Aria Packages
Updates the Angular Aria API JSON files in `adev/src/content/aria`, used for the [angular.dev API](https://angular.dev/api) pages.
## CDK Packages
Updates the Angular CDK API JSON files in `adev/src/content/cdk`, used for the [angular.dev API](https://angular.dev/api) pages.
## CLI Help Pages
Updates the Angular CLI help JSON files in `adev/src/content/cli`, used for the [angular.dev CLI](https://angular.dev/cli) pages.
@@ -1,41 +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
*/
//tslint:disable:no-console
import {execSync} from 'node:child_process';
import {join} from 'node:path';
import {updateAssets} from './update-assets.mjs';
async function main() {
await updateAssets({
repo: 'angular/aria-builds',
assetsPath: '_adev_assets',
destPath: join(import.meta.dirname, '../../src/content/aria'),
});
await updateAssets({
repo: 'angular/cdk-builds',
assetsPath: '_adev_assets',
destPath: join(import.meta.dirname, '../../src/content/cdk'),
});
await updateAssets({
repo: 'angular/cli-builds',
assetsPath: 'help',
destPath: join(import.meta.dirname, '../../src/content/cli'),
});
console.log('\n-----------------------------------------------');
console.log('Change list');
console.log('-----------------------------------------------\n');
execSync(`git status --porcelain`, {stdio: 'inherit'});
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
@@ -1,132 +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
*/
//tslint:disable:no-console
import assert from 'node:assert';
import {execSync} from 'node:child_process';
import {existsSync, constants as fsConstants} from 'node:fs';
import {
copyFile,
mkdtemp,
readdir,
readFile,
realpath,
rm,
unlink,
writeFile,
} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {GithubClient} from './github-client.mjs';
export async function updateAssets({repo, assetsPath, destPath}) {
console.log('\n-----------------------------------------------');
console.log(`Processing: ${repo}`);
console.log('-----------------------------------------------\n');
const buildInfoPath = join(destPath, '_build-info.json');
if (!existsSync(buildInfoPath)) {
throw new Error(`${buildInfoPath} does not exist.`);
}
assert(process.env.GITHUB_REF);
const currentBranch = process.env.GITHUB_REF;
const {sha: storedSha, branchName: storedBranch} = JSON.parse(
await readFile(buildInfoPath, 'utf-8'),
);
assert(process.env.ANGULAR_READONLY_GITHUB_TOKEN);
const githubApi = new GithubClient(
repo,
process.env.ANGULAR_READONLY_GITHUB_TOKEN,
'ADEV_Cross_Repo_Docs_Update',
);
let downstreamBranch = currentBranch;
let latestSha = await githubApi.getShaForBranch(currentBranch);
if (
latestSha.includes('No commit') &&
currentBranch !== 'refs/heads/main' &&
currentBranch !== storedBranch
) {
// In some cases, such as when a new branch is created for a feature,
// the branch may not exist in the downstream repo. For example, during an
// exceptional minor release (e.g. FW 20.3.x and Components: 20.2.x).
// In such scenarios, we fallback to the last known branch.
latestSha = await githubApi.getShaForBranch(storedBranch);
downstreamBranch = storedBranch;
}
console.log(`Comparing ${storedSha}...${latestSha}.`);
const affectedFiles = await githubApi.getAffectedFiles(storedSha, latestSha);
const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`));
let shaWhenFilesChanged;
if (changedFiles.length > 0) {
console.log(
`The below files changed between ${storedSha} and ${latestSha}:\n` +
changedFiles.map((f) => '* ' + f).join('\n'),
);
const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'update-assets-')));
try {
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
execSync('git init', execOptions);
execSync(`git remote add origin https://github.com/${repo}.git`, execOptions);
// fetch a commit
execSync(`git fetch origin ${latestSha}`, execOptions);
// reset this repository's main branch to the commit of interest
execSync('git reset --hard FETCH_HEAD', execOptions);
// get sha when files where changed
shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, {
encoding: 'utf8',
cwd: temporaryDir,
stdio: ['ignore', 'pipe', 'ignore'],
}).trim();
// Delete existing asset files.
const apiFilesUnlink = (await readdir(destPath))
.filter((f) => f.endsWith('.json'))
.map((f) => unlink(join(destPath, f)));
await Promise.allSettled(apiFilesUnlink);
// Copy new asset files
const tempAssetsDir = join(temporaryDir, assetsPath);
const assetFilesCopy = (await readdir(tempAssetsDir)).map((f) => {
const src = join(tempAssetsDir, f);
const dest = join(destPath, f);
return copyFile(src, dest, fsConstants.COPYFILE_FICLONE);
});
await Promise.allSettled(assetFilesCopy);
} finally {
await rm(temporaryDir, {force: true, recursive: true});
}
console.log(`Successfully updated asset files in '${destPath}'.\n`);
} else {
console.log(`No '${assetsPath}/**' files changed between ${storedSha} and ${latestSha}.`);
}
// Write SHA to file.
await writeFile(
buildInfoPath,
JSON.stringify(
{
branchName: downstreamBranch,
sha: shaWhenFilesChanged ?? storedSha,
},
undefined,
2,
),
);
}
-1
View File
@@ -20,7 +20,6 @@ ts_project(
"//adev/shared-docs/components/search-dialog",
"//adev/shared-docs/components/select",
"//adev/shared-docs/components/slide-toggle",
"//adev/shared-docs/components/tab-group",
"//adev/shared-docs/components/table-of-contents",
"//adev/shared-docs/components/text-field",
"//adev/shared-docs/components/top-level-banner",
@@ -1,52 +1,52 @@
<!-- Algolia logo -->
<svg
id="Layer_1"
class="docs-algolia-logo"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 2196.2 500"
id="Layer_1"
class="docs-algolia-logo"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 2196.2 500"
>
<defs>
<style>
.cls-1,
.cls-2 {
fill: #003dff;
}
.cls-2 {
fill-rule: evenodd;
}
</style>
</defs>
<path
class="cls-2"
d="M1070.38,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
/>
<rect class="cls-1" x="1845.88" y="104.73" width="62.58" height="277.9" rx="5.9" ry="5.9" />
<path
class="cls-2"
d="M1851.78,71.38h50.77c3.26,0,5.9-2.64,5.9-5.9V5.9c0-3.62-3.24-6.39-6.82-5.83l-50.77,7.95c-2.87,.45-4.99,2.92-4.99,5.83v51.62c0,3.26,2.64,5.9,5.9,5.9Z"
/>
<path
class="cls-2"
d="M1764.03,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
/>
<path
class="cls-2"
d="M1631.95,142.72c-11.14-12.25-24.83-21.65-40.78-28.31-15.92-6.53-33.26-9.85-52.07-9.85-18.78,0-36.15,3.17-51.92,9.85-15.59,6.66-29.29,16.05-40.76,28.31-11.47,12.23-20.38,26.87-26.76,44.03-6.38,17.17-9.24,37.37-9.24,58.36,0,20.99,3.19,36.87,9.55,54.21,6.38,17.32,15.14,32.11,26.45,44.36,11.29,12.23,24.83,21.62,40.6,28.46,15.77,6.83,40.12,10.33,52.4,10.48,12.25,0,36.78-3.82,52.7-10.48,15.92-6.68,29.46-16.23,40.78-28.46,11.29-12.25,20.05-27.04,26.25-44.36,6.22-17.34,9.24-33.22,9.24-54.21,0-20.99-3.34-41.19-10.03-58.36-6.38-17.17-15.14-31.8-26.43-44.03Zm-44.43,163.75c-11.47,15.75-27.56,23.7-48.09,23.7-20.55,0-36.63-7.8-48.1-23.7-11.47-15.75-17.21-34.01-17.21-61.2,0-26.89,5.59-49.14,17.06-64.87,11.45-15.75,27.54-23.52,48.07-23.52,20.55,0,36.63,7.78,48.09,23.52,11.47,15.57,17.36,37.98,17.36,64.87,0,27.19-5.72,45.3-17.19,61.2Z"
/>
<path
class="cls-2"
d="M894.42,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
/>
<path
class="cls-2"
d="M2133.97,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
/>
<path
class="cls-2"
d="M1314.05,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-11.79,18.34-19.6,39.64-22.11,62.59-.58,5.3-.88,10.68-.88,16.14s.31,11.15,.93,16.59c4.28,38.09,23.14,71.61,50.66,94.52,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47h0c17.99,0,34.61-5.93,48.16-15.97,16.29-11.58,28.88-28.54,34.48-47.75v50.26h-.11v11.08c0,21.84-5.71,38.27-17.34,49.36-11.61,11.08-31.04,16.63-58.25,16.63-11.12,0-28.79-.59-46.6-2.41-2.83-.29-5.46,1.5-6.27,4.22l-12.78,43.11c-1.02,3.46,1.27,7.02,4.83,7.53,21.52,3.08,42.52,4.68,54.65,4.68,48.91,0,85.16-10.75,108.89-32.21,21.48-19.41,33.15-48.89,35.2-88.52V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,64.1s.65,139.13,0,143.36c-12.08,9.77-27.11,13.59-43.49,14.7-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-1.32,0-2.63-.03-3.94-.1-40.41-2.11-74.52-37.26-74.52-79.38,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33Z"
/>
<path
class="cls-1"
d="M249.83,0C113.3,0,2,110.09,.03,246.16c-2,138.19,110.12,252.7,248.33,253.5,42.68,.25,83.79-10.19,120.3-30.03,3.56-1.93,4.11-6.83,1.08-9.51l-23.38-20.72c-4.75-4.21-11.51-5.4-17.36-2.92-25.48,10.84-53.17,16.38-81.71,16.03-111.68-1.37-201.91-94.29-200.13-205.96,1.76-110.26,92-199.41,202.67-199.41h202.69V407.41l-115-102.18c-3.72-3.31-9.42-2.66-12.42,1.31-18.46,24.44-48.53,39.64-81.93,37.34-46.33-3.2-83.87-40.5-87.34-86.81-4.15-55.24,39.63-101.52,94-101.52,49.18,0,89.68,37.85,93.91,85.95,.38,4.28,2.31,8.27,5.52,11.12l29.95,26.55c3.4,3.01,8.79,1.17,9.63-3.3,2.16-11.55,2.92-23.58,2.07-35.92-4.82-70.34-61.8-126.93-132.17-131.26-80.68-4.97-148.13,58.14-150.27,137.25-2.09,77.1,61.08,143.56,138.19,145.26,32.19,.71,62.03-9.41,86.14-26.95l150.26,133.2c6.44,5.71,16.61,1.14,16.61-7.47V9.48C499.66,4.25,495.42,0,490.18,0H249.83Z"
/>
<defs>
<style>
.cls-1,
.cls-2 {
fill: #003dff;
}
.cls-2 {
fill-rule: evenodd;
}
</style>
</defs>
<path
class="cls-2"
d="M1070.38,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
/>
<rect class="cls-1" x="1845.88" y="104.73" width="62.58" height="277.9" rx="5.9" ry="5.9" />
<path
class="cls-2"
d="M1851.78,71.38h50.77c3.26,0,5.9-2.64,5.9-5.9V5.9c0-3.62-3.24-6.39-6.82-5.83l-50.77,7.95c-2.87,.45-4.99,2.92-4.99,5.83v51.62c0,3.26,2.64,5.9,5.9,5.9Z"
/>
<path
class="cls-2"
d="M1764.03,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
/>
<path
class="cls-2"
d="M1631.95,142.72c-11.14-12.25-24.83-21.65-40.78-28.31-15.92-6.53-33.26-9.85-52.07-9.85-18.78,0-36.15,3.17-51.92,9.85-15.59,6.66-29.29,16.05-40.76,28.31-11.47,12.23-20.38,26.87-26.76,44.03-6.38,17.17-9.24,37.37-9.24,58.36,0,20.99,3.19,36.87,9.55,54.21,6.38,17.32,15.14,32.11,26.45,44.36,11.29,12.23,24.83,21.62,40.6,28.46,15.77,6.83,40.12,10.33,52.4,10.48,12.25,0,36.78-3.82,52.7-10.48,15.92-6.68,29.46-16.23,40.78-28.46,11.29-12.25,20.05-27.04,26.25-44.36,6.22-17.34,9.24-33.22,9.24-54.21,0-20.99-3.34-41.19-10.03-58.36-6.38-17.17-15.14-31.8-26.43-44.03Zm-44.43,163.75c-11.47,15.75-27.56,23.7-48.09,23.7-20.55,0-36.63-7.8-48.1-23.7-11.47-15.75-17.21-34.01-17.21-61.2,0-26.89,5.59-49.14,17.06-64.87,11.45-15.75,27.54-23.52,48.07-23.52,20.55,0,36.63,7.78,48.09,23.52,11.47,15.57,17.36,37.98,17.36,64.87,0,27.19-5.72,45.3-17.19,61.2Z"
/>
<path
class="cls-2"
d="M894.42,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
/>
<path
class="cls-2"
d="M2133.97,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
/>
<path
class="cls-2"
d="M1314.05,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-11.79,18.34-19.6,39.64-22.11,62.59-.58,5.3-.88,10.68-.88,16.14s.31,11.15,.93,16.59c4.28,38.09,23.14,71.61,50.66,94.52,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47h0c17.99,0,34.61-5.93,48.16-15.97,16.29-11.58,28.88-28.54,34.48-47.75v50.26h-.11v11.08c0,21.84-5.71,38.27-17.34,49.36-11.61,11.08-31.04,16.63-58.25,16.63-11.12,0-28.79-.59-46.6-2.41-2.83-.29-5.46,1.5-6.27,4.22l-12.78,43.11c-1.02,3.46,1.27,7.02,4.83,7.53,21.52,3.08,42.52,4.68,54.65,4.68,48.91,0,85.16-10.75,108.89-32.21,21.48-19.41,33.15-48.89,35.2-88.52V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,64.1s.65,139.13,0,143.36c-12.08,9.77-27.11,13.59-43.49,14.7-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-1.32,0-2.63-.03-3.94-.1-40.41-2.11-74.52-37.26-74.52-79.38,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33Z"
/>
<path
class="cls-1"
d="M249.83,0C113.3,0,2,110.09,.03,246.16c-2,138.19,110.12,252.7,248.33,253.5,42.68,.25,83.79-10.19,120.3-30.03,3.56-1.93,4.11-6.83,1.08-9.51l-23.38-20.72c-4.75-4.21-11.51-5.4-17.36-2.92-25.48,10.84-53.17,16.38-81.71,16.03-111.68-1.37-201.91-94.29-200.13-205.96,1.76-110.26,92-199.41,202.67-199.41h202.69V407.41l-115-102.18c-3.72-3.31-9.42-2.66-12.42,1.31-18.46,24.44-48.53,39.64-81.93,37.34-46.33-3.2-83.87-40.5-87.34-86.81-4.15-55.24,39.63-101.52,94-101.52,49.18,0,89.68,37.85,93.91,85.95,.38,4.28,2.31,8.27,5.52,11.12l29.95,26.55c3.4,3.01,8.79,1.17,9.63-3.3,2.16-11.55,2.92-23.58,2.07-35.92-4.82-70.34-61.8-126.93-132.17-131.26-80.68-4.97-148.13,58.14-150.27,137.25-2.09,77.1,61.08,143.56,138.19,145.26,32.19,.71,62.03-9.41,86.14-26.95l150.26,133.2c6.44,5.71,16.61,1.14,16.61-7.47V9.48C499.66,4.25,495.42,0,490.18,0H249.83Z"
/>
</svg>

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

@@ -21,7 +21,7 @@ import {RouterLink} from '@angular/router';
export class Breadcrumb {
private readonly navigationState = inject(NavigationState);
readonly breadcrumbItems = computed(() => {
breadcrumbItems = computed(() => {
const breadcrumbs: NavigationItem[] = [];
let activeItem = this.navigationState.activeNavigationItem()?.parent;
@@ -1,28 +1,22 @@
@if (!hasAccepted()) {
<div class="docs-cookies-popup docs-invert-mode">
<p>This site uses cookies from Google to deliver its services and to analyze traffic.</p>
<div class="docs-cookies-popup docs-invert-mode">
<p>This site uses cookies from Google to deliver its services and to analyze traffic.</p>
<div>
<a
href="https://policies.google.com/technologies/cookies"
target="_blank"
rel="noopener"
class="docs-primary-btn"
aria-label="Learn more about how Google uses cookies"
text="Learn more"
>
<div>
<a href="https://policies.google.com/technologies/cookies" target="_blank" rel="noopener">
<button class="docs-primary-btn" [attr.text]="'Learn more'" aria-label="Learn More">
Learn more
</a>
<button
type="button"
(click)="accept()"
class="docs-primary-btn"
text="Ok, Got it"
aria-label="Ok, Got it"
>
Ok, Got it
</button>
</div>
</a>
<button
type="button"
(click)="accept()"
class="docs-primary-btn"
[attr.text]="'Ok, Got it'"
aria-label="Ok, Got it"
>
Ok, Got it
</button>
</div>
</div>
}
@@ -11,7 +11,7 @@ import {LOCAL_STORAGE} from '../../providers/index';
import {setCookieConsent} from '../../utils';
/**
* Declare gtag as part of the window in this file as gtag is expected to already be loaded.
* Decelare gtag as part of the window in this file as gtag is expected to already be loaded.
*/
declare const window: Window & typeof globalThis & {gtag?: Function};
@@ -27,7 +27,7 @@ export class CookiePopup {
private readonly localStorage = inject(LOCAL_STORAGE);
/** Whether the user has accepted the cookie disclaimer. */
readonly hasAccepted = signal(false);
hasAccepted = signal<boolean>(false);
constructor() {
// Needs to be in a try/catch, because some browsers will
@@ -1,19 +0,0 @@
load("//adev/shared-docs:defaults.bzl", "ng_project")
ng_project(
name = "copy-link-anchor",
srcs = [
"copy-link-anchor.component.ts",
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/viewers:__pkg__",
],
deps = [
"//adev:node_modules/@angular/cdk",
"//adev:node_modules/@angular/common",
"//adev:node_modules/@angular/core",
"//adev:node_modules/@angular/material",
"//adev/shared-docs/components/icon",
],
)
@@ -1,66 +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
*/
import {ChangeDetectionStrategy, Component, inject, input, signal} from '@angular/core';
import {Clipboard} from '@angular/cdk/clipboard';
import {MatTooltip} from '@angular/material/tooltip';
import {IconComponent} from '../icon/icon.component';
export const CONFIRMATION_DISPLAY_TIME_MS = 1000;
@Component({
selector: 'docs-copy-link-button',
template: `<docs-icon>{{ showCopySuccess() ? 'check' : 'link' }}</docs-icon>`,
styles: `
:host {
cursor: pointer;
padding: 0;
margin-left: 8px;
display: inline-flex;
align-items: center;
opacity: 0;
transition: opacity 0.3s ease;
color: var(--quaternary-contrast);
vertical-align: middle;
}
:host(.docs-copy-link-success) {
color: var(--bright-blue);
}
`,
hostDirectives: [
{
directive: MatTooltip,
inputs: ['matTooltip'],
},
],
host: {
'[ariaLabel]': '"Copy link to " + label()',
'(click)': 'copyLink()',
'matTooltipPosition': 'above',
'[class.docs-copy-link-success]': 'showCopySuccess()',
},
imports: [IconComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CopyLinkButton {
readonly href = input.required<string>();
readonly label = input.required<string>();
protected readonly showCopySuccess = signal(false);
private readonly clipboard = inject(Clipboard);
copyLink(): void {
const link = `${window.location.origin}${this.href()}`;
this.clipboard.copy(link);
this.showCopySuccess.set(true);
setTimeout(() => {
this.showCopySuccess.set(false);
}, CONFIRMATION_DISPLAY_TIME_MS);
}
}
@@ -56,6 +56,24 @@ describe('CopySourceCodeButton', () => {
expect(copySpy.calls.argsFor(0)[0].trim()).toBe(expectedCodeToBeCopied);
});
it('should not copy lines marked as deleted when code snippet contains diff', async () => {
const codeInHtmlFormat = `
<code>
<div class="line remove"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> *<span class="hljs-attr">ngFor</span>=<span class="hljs-string">"let product of products"</span>&gt;</span></div>
<div class="line add"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> *<span class="hljs-attr">ngFor</span>=<span class="hljs-string">"let product of products()"</span>&gt;</span></div>
</code>
`;
const expectedCodeToBeCopied = `<div *ngFor="let product of products()">`;
component.code.set(codeInHtmlFormat);
await fixture.whenStable();
const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement;
button.click();
expect(copySpy.calls.argsFor(0)[0].trim()).toBe(expectedCodeToBeCopied);
});
it(`should set ${SUCCESSFULLY_COPY_CLASS_NAME} for ${CONFIRMATION_DISPLAY_TIME_MS} ms when copy was executed properly`, () => {
const clock = jasmine.clock().install();
component.code.set('example');
@@ -15,6 +15,7 @@ import {
inject,
signal,
} from '@angular/core';
import {CommonModule} from '@angular/common';
import {Clipboard} from '@angular/cdk/clipboard';
import {IconComponent} from '../icon/icon.component';
@@ -23,7 +24,7 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 2000;
@Component({
selector: 'button[docs-copy-source-code]',
imports: [IconComponent],
imports: [CommonModule, IconComponent],
templateUrl: './copy-source-code-button.component.html',
host: {
'type': 'button',
@@ -12,7 +12,6 @@ ng_project(
],
visibility = [
"//adev/shared-docs/components:__pkg__",
"//adev/shared-docs/components/copy-link-anchor:__pkg__",
"//adev/shared-docs/components/copy-source-code-button:__pkg__",
"//adev/shared-docs/components/navigation-list:__pkg__",
"//adev/shared-docs/components/table-of-contents:__pkg__",
@@ -20,7 +20,6 @@ ng_project(
"//adev:node_modules/@angular/material",
"//adev:node_modules/@angular/router",
"//adev/shared-docs/components/icon",
"//adev/shared-docs/components/slide-toggle",
"//adev/shared-docs/interfaces",
"//adev/shared-docs/pipes",
"//adev/shared-docs/services",
@@ -3,118 +3,106 @@
class="docs-navigation-list docs-faceted-list"
[class.docs-navigation-list-dropdown]="isDropdownView()"
>
@for (itemGroup of groupItems(navigationItems); track $index) {
@let groupLabel = itemGroup[0];
@let items = itemGroup[1];
@if (groupLabel) {
<li class="docs-navigation-group">{{groupLabel | titlecase}}</li>
}
@for (item of items; track $index) {
<li class="docs-faceted-list-item">
@let itemLabel = item.label!;
@if (item.path) {
@if (item.isExternal) {
<a
[href]="item.path"
target="_blank"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@for (item of navigationItems; track $index) {
<li class="docs-faceted-list-item">
@if (item.path) {
@if (item.isExternal) {
<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"
>
<span
[class.docs-external-link]="item.isExternal"
class="docs-faceted-list-item-text"
>
{{ item.label }}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
{{ item.label }}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
@if (item.children && item.level! > 1 && !item.isExpanded) {
<docs-icon>chevron_right</docs-icon>
}
</a>
} @else {
<a
[routerLink]="'/' + item.path"
routerLinkActive="docs-faceted-list-item-active"
[routerLinkActiveOptions]="{ paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }"
(click)="emitClickOnLink()"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
>
<span class="docs-faceted-list-item-text">
{{item.label}}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
@if (item.children && !item.isExpanded) {
<docs-icon>chevron_right</docs-icon>
}
</a>
}
@if (item.children && item.level! > 1 && !item.isExpanded) {
<docs-icon>chevron_right</docs-icon>
}
</a>
} @else {
<!-- Nav Section Header -->
@if (item.level !== collapsableLevel() && item.level !== expandableLevel()) {
<div
class="docs-secondary-nav-header"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
>
<span class="docs-faceted-list-item-text">
{{item.label}}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
</div>
}
<a
[routerLink]="'/' + item.path"
routerLinkActive="docs-faceted-list-item-active"
[routerLinkActiveOptions]="{ exact: true }"
(click)="emitClickOnLink()"
[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}}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
<!-- Nav Button Expand/Collapse -->
@if (
(item.children && item.level === expandableLevel()) || item.level === collapsableLevel()
) {
<button
type="button"
(click)="toggle(item)"
[attr.aria-label]="(item.isExpanded ? 'Collapse' : 'Expand') + ' ' + item.label"
[attr.aria-expanded]="item.isExpanded"
class="docs-secondary-nav-button"
[class.docs-faceted-list-item-active]="item | isActiveNavigationItem: activeItem()"
[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"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.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 class="docs-faceted-list-item-text">
{{ item.label }}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
</button>
}
@if (item.children && !item.isExpanded) {
<docs-icon>chevron_right</docs-icon>
}
</a>
}
} @else {
<!-- Nav Section Header -->
@if (item.level !== collapsableLevel() && item.level !== expandableLevel()) {
<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}}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
</div>
}
<!-- Nav Button Expand/Collapse -->
@if (
item.level && displayItemsToLevel() > item.level && item.children && item.children.length > 0
(item.children && item.level === expandableLevel()) || item.level === collapsableLevel()
) {
<ng-container
*ngTemplateOutlet="navigationList; context: {$implicit: item.children, level: item.level}"
/>
<button
type="button"
(click)="toggle(item)"
[attr.aria-label]="(item.isExpanded ? 'Collapse' : 'Expand') + ' ' + item.label"
[attr.aria-expanded]="item.isExpanded"
class="docs-secondary-nav-button"
[class.docs-faceted-list-item-active]="item | isActiveNavigationItem: activeItem()"
[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"
[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 class="docs-faceted-list-item-text">
{{ item.label }}
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
</span>
</button>
}
</li>
}
}
@if (displayItemsToLevel() > item.level && item.children?.length > 0) {
<ng-container *ngTemplateOutlet="navigationList; context: {$implicit: item.children}" />
}
</li>
}
</ul>
</ng-template>
@@ -49,7 +49,6 @@
max-width: calc(100% - 1rem);
overflow: hidden;
text-overflow: ellipsis;
text-wrap: balance;
}
.docs-nav-item-has-icon {
@@ -186,11 +185,3 @@ a,
white-space: pre;
}
}
.docs-navigation-group {
padding: 0.5rem 0.5rem 0.5rem 1rem;
background: var(--septenary-contrast);
&:not(:first-child) {
margin-top: 2rem;
}
}
@@ -12,9 +12,8 @@ import {NavigationState} from '../../services/index';
import {RouterLink, RouterLinkActive} from '@angular/router';
import {IconComponent} from '../icon/icon.component';
import {IsActiveNavigationItem} from '../../pipes';
import {NgTemplateOutlet, TitleCasePipe} from '@angular/common';
import {NgTemplateOutlet} from '@angular/common';
import {MatTooltipModule} from '@angular/material/tooltip';
import {ReactiveFormsModule} from '@angular/forms';
@Component({
selector: 'docs-navigation-list',
@@ -25,8 +24,6 @@ import {ReactiveFormsModule} from '@angular/forms';
IsActiveNavigationItem,
NgTemplateOutlet,
MatTooltipModule,
ReactiveFormsModule,
TitleCasePipe,
],
templateUrl: './navigation-list.component.html',
styleUrls: ['./navigation-list.component.scss'],
@@ -34,10 +31,10 @@ import {ReactiveFormsModule} from '@angular/forms';
})
export class NavigationList {
readonly navigationItems = input.required<NavigationItem[]>();
readonly displayItemsToLevel = input(2);
readonly displayItemsToLevel = input<number>(2);
readonly collapsableLevel = input<number | undefined>();
readonly expandableLevel = input(2);
readonly isDropdownView = input(false);
readonly expandableLevel = input<number>(2);
readonly isDropdownView = input<boolean>(false);
readonly linkClicked = output<void>();
@@ -59,33 +56,4 @@ export class NavigationList {
emitClickOnLink(): void {
this.linkClicked.emit();
}
private hasCategories(items: NavigationItem[]): boolean {
return items.some((item) => !!item.category);
}
protected groupItems(items: NavigationItem[]): Map<string, NavigationItem[]> {
const hasCategories = this.hasCategories(items);
if (hasCategories) {
const others: NavigationItem[] = [];
const categorizedItems = new Map<string, NavigationItem[]>();
for (const item of items) {
const category = item.category || 'Other';
if (category === 'Other') {
others.push(item);
continue;
}
if (!categorizedItems.has(category)) {
categorizedItems.set(category, []);
}
categorizedItems.get(category)!.push(item);
}
if (others.length) {
categorizedItems.set('Other', others);
}
return categorizedItems;
} else {
return new Map([['', items]]);
}
}
}
@@ -12,49 +12,35 @@
@if (searchResults().length) {
<ul class="docs-search-results docs-mini-scroll-track">
@for (result of searchResults(); track result.id) {
<li docsSearchItem [item]="result" class="docs-search-result">
<li docsSearchItem [item]="result">
<a
[routerLink]="'/' + result.url | relativeLink: 'pathname'"
[fragment]="result.url | relativeLink: 'hash'"
(click)="history.addItem(result)"
>
<div>
<p class="docs-search-result__label">
<div class="docs-result-icon-and-type">
<!-- Icon -->
<span class="docs-search-result-icon" aria-hidden="true">
<i role="presentation" class="material-symbols-outlined docs-icon-small">
{{ result.type === 'code' ? 'code' : 'description'}}
</i>
</span>
<!-- Page title -->
<span [innerHtml]="result.labelHtml"></span>
@if (result.package) {
<span
[innerHTML]="result.package"
class="docs-search-result__label__package"
></span>
}
</p>
<!-- Results type -->
<span class="docs-search-results__type" [innerHtml]="result.labelHtml"></span>
</div>
@if (result.subLabelHtml) {
<p class="docs-search-result__sub-label">
<span class="docs-search-result-icon" aria-hidden="true">
<i role="presentation" class="material-symbols-outlined docs-icon-small">
grid_3x3
</i>
</span>
<span [innerHtml]="result.subLabelHtml"></span>
</p>
}
@if (result.contentHtml) {
<p class="docs-search-result__content" [innerHtml]="result.contentHtml"></p>
<span
class="docs-search-results__type docs-search-results__lvl2"
[innerHtml]="result.subLabelHtml"
>
</span>
}
</div>
<p class="docs-search-result__category">
{{ result.category }}
</p>
<!-- Page title -->
<span class="docs-result-page-title">{{ result.category }}</span>
</a>
</li>
}
@@ -38,21 +38,11 @@ dialog {
margin: 0;
border-block-end: 1px solid var(--senary-contrast);
.docs-search-result {
li {
border-inline-start: 2px solid var(--senary-contrast);
margin-inline-start: 1rem;
display: block;
font-size: 0.875rem;
.docs-search-result-icon {
display: inline-block;
i {
display: flex;
align-items: center;
font-size: 1.2rem;
}
}
padding-inline-end: 1rem;
padding-block: 0.25rem;
/**
* This rule needs ng-deep to be applied to elements that are created via a [innerHTML] binding
@@ -71,50 +61,17 @@ dialog {
color: var(--secondary-contrast);
display: flex;
justify-content: space-between;
padding: 1rem;
gap: 0.5rem;
}
&__label,
&__sub-label,
&__content {
transition: color 0.3s ease;
}
&__label,
&__sub-label {
display: flex;
align-items: center;
gap: 0.75rem;
margin: 0;
}
&__label {
font-weight: 600;
flex-wrap: wrap;
&__package {
font-size: 0.75rem;
.docs-search-result-icon {
i {
display: flex;
align-items: center;
font-size: 1.2rem;
}
}
}
&__content,
&__category {
margin: 0;
}
&__sub-label,
&__content {
margin-block-start: 0.375rem;
margin-inline-start: 2rem;
color: var(--quaternary-contrast);
}
&__category {
font-weight: 400;
color: var(--quaternary-contrast);
}
&.active {
background-color: var(--septenary-contrast); // stylelint-disable-line
}
@@ -123,14 +80,44 @@ dialog {
&.active {
background-color: var(--octonary-contrast); // stylelint-disable-line
border-inline-start: 2px solid var(--primary-contrast);
.docs-search-result__label,
.docs-search-result__sub-label,
.docs-search-result__content {
color: var(--primary-contrast);
a {
span:not(.docs-result-page-title),
.docs-search-results__type {
color: var(--primary-contrast);
i {
color: var(--primary-contrast);
}
}
}
}
}
.docs-search-result-icon,
.docs-search-results__type,
.docs-result-page-title {
color: var(--quaternary-contrast);
display: inline-block;
font-size: 0.875rem;
transition: color 0.3s ease;
padding: 0.75rem;
padding-inline-end: 0;
}
.docs-search-results__lvl2 {
display: inline-block;
margin-inline-start: 2rem;
padding-block-start: 0;
}
.docs-search-results__lvl3 {
margin-inline-start: 2rem;
padding-block-start: 0;
}
}
.docs-result-page-title {
font-size: 0.875rem;
font-weight: 400;
}
}
@@ -140,6 +127,14 @@ dialog {
color: var(--gray-400);
}
.docs-result-icon-and-type {
display: flex;
.docs-search-results__type {
padding-inline-start: 0;
}
}
.docs-search-footer {
display: flex;
align-items: center;
@@ -51,10 +51,10 @@ import {SearchHistoryComponent} from '../search-history/search-history.component
styleUrls: ['./search-dialog.component.scss'],
})
export class SearchDialog {
readonly onClose = output();
readonly dialog = viewChild.required<ElementRef<HTMLDialogElement>>('searchDialog');
readonly items = viewChildren(SearchItem);
readonly textField = viewChild(TextField);
onClose = output();
dialog = viewChild.required<ElementRef<HTMLDialogElement>>('searchDialog');
items = viewChildren(SearchItem);
textField = viewChild(TextField);
readonly history = inject(SearchHistory);
private readonly search = inject(Search);
@@ -67,9 +67,9 @@ export class SearchDialog {
this.injector,
).withWrap();
readonly searchQuery = this.search.searchQuery;
readonly resultsResource = this.search.resultsResource;
readonly searchResults = this.search.searchResults;
searchQuery = this.search.searchQuery;
resultsResource = this.search.resultsResource;
searchResults = this.search.searchResults;
// We use a FormControl instead of relying on NgModel+signal to avoid
// the issue https://github.com/angular/angular/issues/13568
@@ -22,7 +22,6 @@ ng_project(
],
deps = [
"//adev:node_modules/@angular/cdk",
"//adev:node_modules/@angular/common",
"//adev:node_modules/@angular/core",
"//adev:node_modules/@angular/router",
"//adev/shared-docs/directives",
@@ -1,31 +1,17 @@
<ng-template #searchItem let-item="item" let-type="type">
<i role="presentation" class="material-symbols-outlined type-icon" aria-hidden="true">
{{ type === 'recent' ? 'history' : 'star' }}
</i>
<span [innerHTML]="item.labelHtml" class="label"></span>
@if (item.subLabelHtml) {
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> arrow_right </i>
<span [innerHTML]="item.subLabelHtml" class="sub-label"></span>
}
</ng-template>
@let items = history.items();
@if (items.recent.length) {
<p class="title">Recent</p>
<ul class="history-results recent">
@for (item of items.recent; track item.id; let idx = $index) {
<li docsSearchItem (mouseenter)="onMouseEnter($event, idx)">
<li docsSearchItem [item]="item" (mouseenter)="onMouseEnter($event, idx)">
<a
[routerLink]="'/' + item.url | relativeLink: 'pathname'"
[fragment]="item.url | relativeLink: 'hash'"
(click)="history.addItem(item)"
>
<ng-container
[ngTemplateOutlet]="searchItem"
[ngTemplateOutletContext]="{item, type: 'recent'}"
/>
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> history </i>
<span [innerHTML]="item.labelHtml"></span>
</a>
<button
@@ -34,9 +20,7 @@
(click)="history.makeFavorite(item)"
title="Make favorite"
>
<i role="presentation" class="material-symbols-outlined type-icon" aria-hidden="true">
star
</i>
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> star </i>
</button>
<button
type="button"
@@ -55,15 +39,17 @@
<p class="title">Favorite</p>
<ul class="history-results favorite">
@for (item of items.favorite; track item.id; let idx = $index) {
<li docsSearchItem (mouseenter)="onMouseEnter($event, items.recent.length + idx)">
<li
docsSearchItem
[item]="item"
(mouseenter)="onMouseEnter($event, items.recent.length + idx)"
>
<a
[routerLink]="'/' + item.url | relativeLink: 'pathname'"
[fragment]="item.url | relativeLink: 'hash'"
>
<ng-container
[ngTemplateOutlet]="searchItem"
[ngTemplateOutletContext]="{item, type: 'favorite'}"
/>
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> star </i>
<span [innerHTML]="item.labelHtml"></span>
</a>
<button
@@ -41,23 +41,9 @@
flex: 1;
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--secondary-contrast);
transition: color 0.3s ease;
white-space: nowrap;
overflow: hidden;
.type-icon {
margin-inline-end: 0.75rem;
}
span {
text-overflow: ellipsis;
overflow: hidden;
&.sub-label {
flex: 1;
}
}
&:hover {
color: var(--primary-contrast);

Some files were not shown because too many files have changed in this diff Show More