From 2d5270abb9d3b59f7cd8dd7f127537cddc4efa08 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:06:14 +0000 Subject: [PATCH 1/3] refactor: modernize array and string last element access using `.at(-1)` Replace usages of old patterns. --- .../tests/behavior/serve-live-reload-proxies_spec.ts | 3 +-- .../build/src/builders/dev-server/tests/execute-fetch.ts | 4 ++-- .../angular/build/src/builders/dev-server/vite/index.ts | 4 +--- .../build/src/tools/vite/middlewares/assets-middleware.ts | 2 +- packages/angular/build/src/utils/project-metadata.ts | 2 +- .../angular/build/src/utils/server-rendering/manifest.ts | 2 +- packages/angular/build/src/utils/url.ts | 6 +++--- .../cli/src/command-builder/utilities/json-schema.ts | 2 +- packages/angular/ssr/src/utils/url.ts | 6 +++--- .../architect/src/jobs/simple-scheduler_spec.ts | 2 +- .../tests/behavior/serve-live-reload-proxies_spec.ts | 3 +-- .../src/builders/dev-server/tests/execute-fetch.ts | 2 +- packages/angular_devkit/core/src/utils/template.ts | 4 +--- packages/angular_devkit/core/src/virtual-fs/path.ts | 2 +- packages/angular_devkit/schematics/src/tree/action.ts | 2 +- packages/angular_devkit/schematics/src/workflow/base.ts | 4 ++-- tests/legacy-cli/e2e_runner.ts | 2 +- 17 files changed, 23 insertions(+), 29 deletions(-) diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts index 65f34bddf94d..efdd749de258 100644 --- a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts +++ b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts @@ -105,8 +105,7 @@ DDIy4xXPW1STWfsmSYJfYW3wa0wk+pJQ3j2cTzkPQQ8gwpvM3U9DJl43uwb37v6I async function goToPageAndWaitForWS(page: Page, url: string): Promise { const baseUrl = url.replace(/^http/, 'ws'); - const socksRequest = - baseUrl[baseUrl.length - 1] === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; + const socksRequest = baseUrl.at(-1) === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; // Create a Chrome dev tools session so that we can capturing websocket request. // https://github.com/puppeteer/puppeteer/issues/2974 diff --git a/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts b/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts index ebbdcf3f1133..75a4e7935ebe 100644 --- a/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts +++ b/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts @@ -28,7 +28,7 @@ export async function executeOnceAndFetch( let content = undefined; if (executionResult.result?.success) { let baseUrl = `${executionResult.result.baseUrl}`; - baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`; + baseUrl = baseUrl.at(-1) === '/' ? baseUrl : `${baseUrl}/`; const resolvedUrl = new URL(url, baseUrl); const originalResponse = await fetch(resolvedUrl, options?.request); response = originalResponse.clone(); @@ -68,7 +68,7 @@ export async function executeOnceAndGet( let content = undefined; if (executionResult.result?.success) { let baseUrl = `${executionResult.result.baseUrl}`; - baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`; + baseUrl = baseUrl.at(-1) === '/' ? baseUrl : `${baseUrl}/`; const resolvedUrl = new URL(url, baseUrl); response = await new Promise((resolve) => diff --git a/packages/angular/build/src/builders/dev-server/vite/index.ts b/packages/angular/build/src/builders/dev-server/vite/index.ts index 75987b2c2cc2..8129daac1ba1 100644 --- a/packages/angular/build/src/builders/dev-server/vite/index.ts +++ b/packages/angular/build/src/builders/dev-server/vite/index.ts @@ -243,9 +243,7 @@ export async function* serveWithVite( const baseHref = result.detail['htmlBaseHref'] as string; // Remove trailing slash serverOptions.servePath = - baseHref !== './' && baseHref[baseHref.length - 1] === '/' - ? baseHref.slice(0, -1) - : baseHref; + baseHref !== './' && baseHref.at(-1) === '/' ? baseHref.slice(0, -1) : baseHref; } assetFiles.clear(); diff --git a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts index 414ec5ca13d1..f0a137f578f8 100644 --- a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts +++ b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts @@ -40,7 +40,7 @@ export function createAngularAssetsMiddleware( // The base of the URL is unused but required to parse the URL. const pathname = pathnameWithoutBasePath(req.url, server.config.base); const extension = extname(pathname); - const pathnameHasTrailingSlash = pathname[pathname.length - 1] === '/'; + const pathnameHasTrailingSlash = pathname.at(-1) === '/'; // Rewrite all build assets to a vite raw fs URL const asset = assets.get(pathname); diff --git a/packages/angular/build/src/utils/project-metadata.ts b/packages/angular/build/src/utils/project-metadata.ts index bc997652fef1..31912d5e9905 100644 --- a/packages/angular/build/src/utils/project-metadata.ts +++ b/packages/angular/build/src/utils/project-metadata.ts @@ -15,7 +15,7 @@ import { join } from 'node:path'; * @returns A normalized path string. */ export function normalizeDirectoryPath(path: string): string { - const last = path[path.length - 1]; + const last = path.at(-1); if (last === '/' || last === '\\') { return path.slice(0, -1); } diff --git a/packages/angular/build/src/utils/server-rendering/manifest.ts b/packages/angular/build/src/utils/server-rendering/manifest.ts index 2dfad0ff2dfb..b01bff38b58f 100644 --- a/packages/angular/build/src/utils/server-rendering/manifest.ts +++ b/packages/angular/build/src/utils/server-rendering/manifest.ts @@ -77,7 +77,7 @@ export function generateAngularServerAppEngineManifest( // Remove trailing slash but retain leading slash. let basePath = baseHref || '/'; - if (basePath.length > 1 && basePath[basePath.length - 1] === '/') { + if (basePath.length > 1 && basePath.at(-1) === '/') { basePath = basePath.slice(0, -1); } diff --git a/packages/angular/build/src/utils/url.ts b/packages/angular/build/src/utils/url.ts index 9edbfb3a3de5..689eac37eab5 100644 --- a/packages/angular/build/src/utils/url.ts +++ b/packages/angular/build/src/utils/url.ts @@ -22,7 +22,7 @@ */ export function stripTrailingSlash(url: string): string { // Check if the last character of the URL is a slash - return url.length > 1 && url[url.length - 1] === '/' ? url.slice(0, -1) : url; + return url.length > 1 && url.at(-1) === '/' ? url.slice(0, -1) : url; } /** @@ -75,7 +75,7 @@ export function addLeadingSlash(url: string): string { */ export function addTrailingSlash(url: string): string { // Check if the URL already end with a slash - return url[url.length - 1] === '/' ? url : `${url}/`; + return url.at(-1) === '/' ? url : `${url}/`; } /** @@ -107,7 +107,7 @@ export function joinUrlParts(...parts: string[]): string { if (part[0] === '/') { normalizedPart = normalizedPart.slice(1); } - if (part[part.length - 1] === '/') { + if (part.at(-1) === '/') { normalizedPart = normalizedPart.slice(0, -1); } if (normalizedPart !== '') { diff --git a/packages/angular/cli/src/command-builder/utilities/json-schema.ts b/packages/angular/cli/src/command-builder/utilities/json-schema.ts index e9fbd7e0e27a..0a4215be8eed 100644 --- a/packages/angular/cli/src/command-builder/utilities/json-schema.ts +++ b/packages/angular/cli/src/command-builder/utilities/json-schema.ts @@ -334,7 +334,7 @@ export async function parseJsonSchemaToOptions( // Skip any non-property items. return; } - const name = ptr[ptr.length - 1]; + const name = ptr.at(-1) as string; const types = getSupportedTypes(current); diff --git a/packages/angular/ssr/src/utils/url.ts b/packages/angular/ssr/src/utils/url.ts index 9b5edede7f8e..55d37ad9a05f 100644 --- a/packages/angular/ssr/src/utils/url.ts +++ b/packages/angular/ssr/src/utils/url.ts @@ -22,7 +22,7 @@ */ export function stripTrailingSlash(url: string): string { // Check if the last character of the URL is a slash - return url.length > 1 && url[url.length - 1] === '/' ? url.slice(0, -1) : url; + return url.length > 1 && url.at(-1) === '/' ? url.slice(0, -1) : url; } /** @@ -75,7 +75,7 @@ export function addLeadingSlash(url: string): string { */ export function addTrailingSlash(url: string): string { // Check if the URL already end with a slash - return url[url.length - 1] === '/' ? url : `${url}/`; + return url.at(-1) === '/' ? url : `${url}/`; } /** @@ -106,7 +106,7 @@ export function joinUrlParts(...parts: string[]): string { if (part[0] === '/') { normalizedPart = normalizedPart.slice(1); } - if (part[part.length - 1] === '/') { + if (part.at(-1) === '/') { normalizedPart = normalizedPart.slice(0, -1); } if (normalizedPart !== '') { diff --git a/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts b/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts index a4b1ee75a922..560927179294 100644 --- a/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts +++ b/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts @@ -168,7 +168,7 @@ describe('SimpleScheduler', () => { // Might be out of order. expect(done).toEqual(jasmine.arrayContaining([1, 2, 3, 4, 5, 6, 7])); // Verify at least partial order. - expect(done[done.length - 1]).toBe(7); + expect(done.at(-1)).toBe(7); expect(done.indexOf(4)).toBeGreaterThan(done.indexOf(1)); expect(done.indexOf(5)).toBeGreaterThan(done.indexOf(2)); expect(done.indexOf(6)).toBeGreaterThan(done.indexOf(3)); diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts index c2a1758f2b5e..09d50dbb4528 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts @@ -108,8 +108,7 @@ async function createProxy(target: string, secure: boolean, ws = true): Promise< async function goToPageAndWaitForWS(page: Page, url: string): Promise { const baseUrl = url.replace(/^http/, 'ws'); - const socksRequest = - baseUrl[baseUrl.length - 1] === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; + const socksRequest = baseUrl.at(-1) === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; // Create a Chrome dev tools session so that we can capturing websocket request. // https://github.com/puppeteer/puppeteer/issues/2974 diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts index eada7975ba88..489ae5501cda 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts @@ -27,7 +27,7 @@ export async function executeOnceAndFetch( let content = undefined; if (executionResult.result?.success) { let baseUrl = `${executionResult.result.baseUrl}`; - baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`; + baseUrl = baseUrl.at(-1) === '/' ? baseUrl : `${baseUrl}/`; const resolvedUrl = new URL(url, baseUrl); const originalResponse = await fetch(resolvedUrl, options?.request); response = originalResponse.clone(); diff --git a/packages/angular_devkit/core/src/utils/template.ts b/packages/angular_devkit/core/src/utils/template.ts index ebd3778fd4e9..89309d676905 100644 --- a/packages/angular_devkit/core/src/utils/template.ts +++ b/packages/angular_devkit/core/src/utils/template.ts @@ -253,9 +253,7 @@ function templateWithSourceMap(ast: TemplateAst, options?: TemplateOptions): str ]), ); - const end = ast.children.length - ? ast.children[ast.children.length - 1].end - : { line: 0, column: 0 }; + const end = ast.children.length ? ast.children.at(-1)?.end : { line: 0, column: 0 }; const nodes = ast.children .reduce((chunk, node) => { let code: string | SourceNode | (SourceNode | string)[] = ''; diff --git a/packages/angular_devkit/core/src/virtual-fs/path.ts b/packages/angular_devkit/core/src/virtual-fs/path.ts index 198b07aa7975..783bfe9d4402 100644 --- a/packages/angular_devkit/core/src/virtual-fs/path.ts +++ b/packages/angular_devkit/core/src/virtual-fs/path.ts @@ -57,7 +57,7 @@ export const NormalizedRoot: Path = NormalizedSep; */ export function split(path: Path): PathFragment[] { const fragments = path.split(NormalizedSep).map((x) => fragment(x)); - if (fragments[fragments.length - 1].length === 0) { + if (fragments.at(-1)?.length === 0) { fragments.pop(); } diff --git a/packages/angular_devkit/schematics/src/tree/action.ts b/packages/angular_devkit/schematics/src/tree/action.ts index 2f5f5e38e900..2d76bcc3662e 100644 --- a/packages/angular_devkit/schematics/src/tree/action.ts +++ b/packages/angular_devkit/schematics/src/tree/action.ts @@ -31,7 +31,7 @@ export class ActionList implements Iterable { this._actions.push({ ...(action as Action), id: _id++, - parent: this._actions[this._actions.length - 1]?.id ?? 0, + parent: this._actions.at(-1)?.id ?? 0, }); } diff --git a/packages/angular_devkit/schematics/src/workflow/base.ts b/packages/angular_devkit/schematics/src/workflow/base.ts index 66f1f20ec379..dcf23ff3f755 100644 --- a/packages/angular_devkit/schematics/src/workflow/base.ts +++ b/packages/angular_devkit/schematics/src/workflow/base.ts @@ -91,7 +91,7 @@ export abstract class BaseWorkflow implements Workflow { } get context(): Readonly { - const maybeContext = this._context[this._context.length - 1]; + const maybeContext = this._context.at(-1); if (!maybeContext) { throw new Error('Cannot get context when workflow is not executing...'); } @@ -146,7 +146,7 @@ export abstract class BaseWorkflow implements Workflow { execute( options: Partial & RequiredWorkflowExecutionContext, ): Observable { - const parentContext = this._context[this._context.length - 1]; + const parentContext = this._context.at(-1); if (!parentContext) { this._lifeCycle.next({ kind: 'start' }); diff --git a/tests/legacy-cli/e2e_runner.ts b/tests/legacy-cli/e2e_runner.ts index 0db1dfb01025..6c9e3179411c 100644 --- a/tests/legacy-cli/e2e_runner.ts +++ b/tests/legacy-cli/e2e_runner.ts @@ -121,7 +121,7 @@ const logger = createConsoleLogger(argv.verbose, process.stdout, process.stderr, const logStack = [logger]; function lastLogger() { - return logStack[logStack.length - 1]; + return logStack.at(-1)!; } // Under bazel the compiled file (.js) and types (.d.ts) are available. From 62988b007ba2aac28bee43b8fa12e61a2c7cf6b2 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:18:20 +0000 Subject: [PATCH 2/3] fixup! refactor: modernize array and string last element access using `.at(-1)` --- packages/angular/ssr/BUILD.bazel | 2 +- tsconfig-build-esm.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/angular/ssr/BUILD.bazel b/packages/angular/ssr/BUILD.bazel index dc70ac3fdf5b..2fd35cf8adc3 100644 --- a/packages/angular/ssr/BUILD.bazel +++ b/packages/angular/ssr/BUILD.bazel @@ -20,7 +20,7 @@ ts_project( ), args = [ "--lib", - "dom,es2020", + "dom,es2022", ], data = [ "//packages/angular/ssr/third_party/beasties:beasties_bundled", diff --git a/tsconfig-build-esm.json b/tsconfig-build-esm.json index 5a548be0a88f..8682ad1fbdc5 100644 --- a/tsconfig-build-esm.json +++ b/tsconfig-build-esm.json @@ -8,7 +8,7 @@ "compilerOptions": { "module": "esnext", "target": "es2022", - "lib": ["es2020"], + "lib": ["es2022"], // don't auto-discover @types/node, it results in a /// Date: Thu, 20 Nov 2025 14:28:06 +0000 Subject: [PATCH 3/3] fixup! refactor: modernize array and string last element access using `.at(-1)` --- packages/angular_devkit/core/src/utils/template.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular_devkit/core/src/utils/template.ts b/packages/angular_devkit/core/src/utils/template.ts index 89309d676905..f28bbf7ad417 100644 --- a/packages/angular_devkit/core/src/utils/template.ts +++ b/packages/angular_devkit/core/src/utils/template.ts @@ -253,7 +253,7 @@ function templateWithSourceMap(ast: TemplateAst, options?: TemplateOptions): str ]), ); - const end = ast.children.length ? ast.children.at(-1)?.end : { line: 0, column: 0 }; + const end = ast.children.at(-1)?.end ?? { line: 0, column: 0 }; const nodes = ast.children .reduce((chunk, node) => { let code: string | SourceNode | (SourceNode | string)[] = '';