/** * @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 {runInEachFileSystem} from '../../src/ngtsc/file_system/testing'; import {loadStandardTestFiles} from '../../src/ngtsc/testing'; import * as esbuild from 'esbuild'; import {NgtscTestEnvironment} from './env'; const testFiles = loadStandardTestFiles({ fakeCommon: true, }); const minifiedDevBuildOptions = { minifySyntax: true, treeShaking: true, keepNames: true, define: {ngDevMode: 'true'}, }; const minifiedProdBuildOptions = { minifySyntax: true, treeShaking: true, keepNames: true, define: {ngDevMode: 'false'}, }; function cleanNewLines(contents: string) { return contents.replace(/\n/g, ' ').replace(/\s+/g, ' '); } runInEachFileSystem(() => { describe('Debug Info Typescript tranformation', () => { let env!: NgtscTestEnvironment; beforeEach(() => { env = NgtscTestEnvironment.setup(testFiles); env.tsconfig({}, {target: 'es2018'}); }); describe('signal', () => { it('should not insert debug info into signal function if not imported from angular core', () => { env.write( 'test.ts', ` declare function signal(value: any): any; const testSignal = signal('Hello World'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into signal function if imported from angular core', () => { env.write( 'test.ts', ` import {signal} from '@angular/core'; const testSignal = signal('Hello World'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `signal('Hello World', ...(ngDevMode ? [{ debugName: "testSignal" }] : /* istanbul ignore next */ []))`, ); }); describe('Variable Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal} from '@angular/core'; const testSignal = signal('Hello World'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('signal("Hello World")'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal} from '@angular/core'; const testSignal = signal('Hello World'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`signal("Hello World", { debugName: "testSignal" });`); }); it('should insert debug info into signal function that already has custom options', async () => { env.write( 'test.ts', ` import {signal} from '@angular/core'; const testSignal = signal('Hello World', { equal: () => true }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `signal('Hello World', { ...(ngDevMode ? { debugName: "testSignal" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for signal function that has custom options', async () => { env.write( 'test.ts', ` import {signal} from '@angular/core'; declare function equal(): boolean; const testSignal = signal('Hello World', { equal }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain(`signal("Hello World", { equal });`); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); }); describe('Property Declaration Case', () => { it('should insert debug info into signal function', () => { env.write( 'test.ts', ` import {signal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal('Hello World'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `signal('Hello World', ...(ngDevMode ? [{ debugName: "testSignal" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal('Hello World'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('signal("Hello World")'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal('Hello World'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`signal("Hello World", { debugName: "testSignal" });`); }); it('should insert debug info into signal function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal('Hello World', { equal: () => true }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `signal('Hello World', { ...(ngDevMode ? { debugName: "testSignal" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for signal function that has custom options', async () => { env.write( 'test.ts', ` import {signal, Component} from '@angular/core'; declare function equal(): boolean; @Component({ template: '' }) class MyComponent { testSignal = signal('Hello World', { equal }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain(`signal("Hello World", { equal });`); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); }); describe('Property Assignment Case', () => { it('should insert debug info into signal function', () => { env.write( 'test.ts', ` import {signal, Component, WritableSignal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; constructor() { this.testSignal = signal('Hello World'); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `signal('Hello World', ...(ngDevMode ? [{ debugName: "testSignal" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, Component, WritableSignal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; constructor() { this.testSignal = signal('Hello World'); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('signal("Hello World")'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, Component, WritableSignal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; constructor() { this.testSignal = signal('Hello World'); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`signal("Hello World", { debugName: "testSignal" });`); }); it('should insert debug info into signal function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, Component, WritableSignal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; constructor() { this.testSignal = signal('Hello World', { equal: () => true }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `signal('Hello World', { ...(ngDevMode ? { debugName: "testSignal" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for signal function that has custom options', async () => { env.write( 'test.ts', ` import {signal, Component, WritableSignal} from '@angular/core'; declare function equal(): boolean; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; constructor() { this.testSignal = signal('Hello World', { equal }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain(`signal("Hello World", { equal });`); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); }); }); describe('computed', () => { it('should not insert debug info into computed function if not imported from angular core', () => { env.write( 'test.ts', ` declare function computed(fn: () => any): any; const testComputed = computed(() => 123); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into computed function if imported from angular core', () => { env.write( 'test.ts', ` import {signal, computed} from '@angular/core'; const testSignal = signal(123); const testComputed = computed(() => testSignal()); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `computed(() => testSignal(), ...(ngDevMode ? [{ debugName: "testComputed" }] : /* istanbul ignore next */ []))`, ); }); describe('Variable Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, computed} from '@angular/core'; const testSignal = signal(123); const testComputed = computed(() => testSignal()); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('computed(() => testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, computed} from '@angular/core'; const testSignal = signal(123); const testComputed = computed(() => testSignal()); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `computed(() => testSignal(), { debugName: "testComputed" })`, ); }); it('should insert debug info into computed function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, computed} from '@angular/core'; const testSignal = signal(123); const testComputed = computed(() => testSignal(), { equal: () => true }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `computed(() => testSignal(), { ...(ngDevMode ? { debugName: "testComputed" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for computed function that has custom options', async () => { env.write( 'test.ts', ` import {signal, computed} from '@angular/core'; declare function equal(): boolean; const testSignal = signal(123); const testComputed = computed(() => testSignal(), { equal }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain(`testComputed = computed(() => testSignal(), { equal })`); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and has custom options', async () => { env.write( 'test.ts', ` import {signal, computed} from '@angular/core'; declare function equal(): boolean; const testSignal = signal(123); const testComputed = computed(() => testSignal(), { equal }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `testComputed = computed(() => testSignal(), { debugName: "testComputed", equal });`, ); }); }); describe('Property Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, computed, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testComputed = computed(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('computed(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, computed, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testComputed = computed(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `computed(() => this.testSignal(), { debugName: "testComputed" })`, ); }); it('should insert debug info into computed function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, computed, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testComputed = computed(() => this.testSignal(), { equal: () => true }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `computed(() => this.testSignal(), { ...(ngDevMode ? { debugName: "testComputed" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for computed function that has custom options', async () => { env.write( 'test.ts', ` import {signal, computed, Component} from '@angular/core'; declare function equal(): boolean; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testComputed = computed(() => this.testSignal(), { equal }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain(`computed(() => this.testSignal(), { equal })`); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and has custom options', async () => { env.write( 'test.ts', ` import {signal, computed, Component} from '@angular/core'; declare function equal(): boolean; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testComputed = computed(() => this.testSignal(), { equal }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `computed(() => this.testSignal(), { debugName: "testComputed", equal });`, ); }); }); describe('Property Assignment Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, computed, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testComputed: Signal; constructor() { this.testSignal = signal(123); this.testComputed = computed(() => this.testSignal()); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('computed(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, computed, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testComputed: Signal; constructor() { this.testSignal = signal(123); this.testComputed = computed(() => this.testSignal()); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `computed(() => this.testSignal(), { debugName: "testComputed" })`, ); }); it('should insert debug info into computed function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, computed, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testComputed: Signal; constructor() { this.testSignal = signal(123); this.testComputed = computed(() => this.testSignal(), { equal: () => true }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `computed(() => this.testSignal(), { ...(ngDevMode ? { debugName: "testComputed" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for computed function that has custom options', async () => { env.write( 'test.ts', ` import {signal, computed, Component, WritableSignal, Signal} from '@angular/core'; declare function equal(): boolean; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testComputed: Signal; constructor() { this.testSignal = signal(123); this.testComputed = computed(() => this.testSignal(), { equal }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain(`computed(() => this.testSignal(), { equal })`); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and has custom options', async () => { env.write( 'test.ts', ` import {signal, computed, Component, WritableSignal, Signal} from '@angular/core'; declare function equal(): boolean; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testComputed: Signal; constructor() { this.testSignal = signal(123); this.testComputed = computed(() => this.testSignal(), { equal }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `computed(() => this.testSignal(), { debugName: "testComputed", equal });`, ); }); }); }); describe('model', () => { it('should not insert debug info into model function if not imported from angular core', () => { env.write( 'test.ts', ` declare function model(value: any): any; import {Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model('Hello World'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into model function if imported from angular core', () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model('Hello World'); testModel2 = model(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `model('Hello World', ...(ngDevMode ? [{ debugName: "testModel" }] : /* istanbul ignore next */ [])`, ); expect(jsContents).toContain( `model(...(ngDevMode ? [undefined, { debugName: "testModel2" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model('Hello World'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('model("Hello World")'); }); describe('.required', () => { it('should insert debug info into .required', () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model.required(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `model.required(...(ngDevMode ? [{ debugName: "testModel" }] : /* istanbul ignore next */ [])`, ); }); it('should insert debug info into .required that already has custom options', () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model.required({ alias: 'testModelAlias' }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `model.required({ ...(ngDevMode ? { debugName: "testModel" } : /* istanbul ignore next */ {}), alias: 'testModelAlias' })`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model.required(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('model.required();'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model.required(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`model.required({ debugName: "testModel" });`); }); it('should tree-shake away debug info if in prod mode with custom options', async () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model.required({ alias: 'testModelAlias' }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('model.required({ alias: "testModelAlias" });'); }); it('should not tree-shake away debug info if in dev mode with custom options', async () => { env.write( 'test.ts', ` import {model, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testModel = model.required({ alias: 'testModelAlias' }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `model.required({ debugName: "testModel", alias: "testModelAlias" });`, ); }); }); }); describe('input', () => { it('should not insert debug info into input function if not imported from angular core', () => { env.write( 'test.ts', ` declare function input(): any; import {Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into input function if imported from angular core', () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `input(...(ngDevMode ? [undefined, { debugName: "testInput" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('input();'); }); describe('.required', () => { it('should insert debug info into .required', () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input.required(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `input.required(...(ngDevMode ? [{ debugName: "testInput" }] : /* istanbul ignore next */ []))`, ); }); it('should insert debug info into .required that already has custom options', () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input.required({ alias: 'testInputAlias' }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `input.required({ ...(ngDevMode ? { debugName: "testInput" } : /* istanbul ignore next */ {}), alias: 'testInputAlias' })`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input.required(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('input.required();'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input.required(); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`input.required({ debugName: "testInput" });`); }); it('should tree-shake away debug info if in prod mode with custom options', async () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input.required({ alias: 'testInputAlias' }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('input.required({ alias: "testInputAlias" });'); }); it('should not tree-shake away debug info if in dev mode with custom options', async () => { env.write( 'test.ts', ` import {input, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testInput = input.required({ alias: 'testInputAlias' }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `input.required({ debugName: "testInput", alias: "testInputAlias" });`, ); }); }); }); describe('viewChild', () => { it('should not insert debug info into viewChild function if not imported from angular core', () => { env.write( 'test.ts', ` declare function viewChild(value: any): any; import {Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChild = viewChild('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into viewChild function if imported from angular core', () => { env.write( 'test.ts', ` import {viewChild, Component} from '@angular/core'; @Component({ selector: 'child-component', template: '' }) class ChildComponent {} @Component({ template: '', imports: [ChildComponent] }) class MyComponent { testViewChild = viewChild('foo'); testViewChildComponent = viewChild(ChildComponent); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `viewChild('foo', ...(ngDevMode ? [{ debugName: "testViewChild" }] : /* istanbul ignore next */ [])`, ); expect(jsContents).toContain( `viewChild(ChildComponent, ...(ngDevMode ? [{ debugName: "testViewChildComponent" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {viewChild, Component} from '@angular/core'; @Component({ selector: 'child-component', template: '' }) class ChildComponent {} @Component({ template: '', imports: [ChildComponent] }) class MyComponent { testViewChild = viewChild('foo'); testViewChildComponent = viewChild(ChildComponent); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain(`viewChild("foo")`); expect(builtContent).toContain(`viewChild(ChildComponent)`); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {viewChild, Component} from '@angular/core'; @Component({ selector: 'child-component', template: '' }) class ChildComponent {} @Component({ template: '', imports: [ChildComponent] }) class MyComponent { testViewChild = viewChild('foo'); testViewChildComponent = viewChild(ChildComponent); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`viewChild("foo", { debugName: "testViewChild" })`); expect(builtContent).toContain( `viewChild(ChildComponent, { debugName: "testViewChildComponent" })`, ); }); it('should tree-shake away debug info if in prod mode with existing options', async () => { env.write( 'test.ts', ` import {viewChild, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChild = viewChild('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('viewChild("foo", { read: ElementRef })'); }); it('should not tree-shake away debug info if in dev mode with existing options', async () => { env.write( 'test.ts', ` import {viewChild, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChild = viewChild('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `viewChild("foo", { debugName: "testViewChild", read: ElementRef })`, ); }); }); describe('viewChildren', () => { it('should not insert debug info into viewChildren function if not imported from angular core', () => { env.write( 'test.ts', ` declare function viewChildren(value: any): any; import {Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChildren = viewChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into viewChildren function if imported from angular core', () => { env.write( 'test.ts', ` import {viewChildren, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChildren = viewChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `viewChildren('foo', ...(ngDevMode ? [{ debugName: "testViewChildren" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {viewChildren, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChildren = viewChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('viewChildren("foo")'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {viewChildren, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChildren = viewChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`viewChildren("foo", { debugName: "testViewChildren" })`); }); it('should tree-shake away debug info if in prod mode with existing options', async () => { env.write( 'test.ts', ` import {viewChildren, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChild = viewChildren('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('viewChildren("foo", { read: ElementRef })'); }); it('should not tree-shake away debug info if in dev mode with existing options', async () => { env.write( 'test.ts', ` import {viewChildren, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testViewChild = viewChildren('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `viewChildren("foo", { debugName: "testViewChild", read: ElementRef })`, ); }); }); describe('contentChild', () => { it('should not insert debug info into contentChild function if not imported from angular core', () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; declare function contentChild(value: any): any; @Component({ template: '' }) class MyComponent { testContentChild = contentChild('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into contentChild function if imported from angular core', () => { env.write( 'test.ts', ` import {contentChild, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChild = contentChild('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `contentChild('foo', ...(ngDevMode ? [{ debugName: "testContentChild" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {contentChild, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChild = contentChild('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('contentChild("foo")'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {contentChild, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChild = contentChild('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain(`contentChild("foo", { debugName: "testContentChild" })`); }); it('should tree-shake away debug info if in prod mode with existing options', async () => { env.write( 'test.ts', ` import {contentChild, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChild = contentChild('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('contentChild("foo", { read: ElementRef })'); }); it('should not tree-shake away debug info if in dev mode with existing options', async () => { env.write( 'test.ts', ` import {contentChild, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChild = contentChild('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `contentChild("foo", { debugName: "testContentChild", read: ElementRef })`, ); }); }); describe('contentChildren', () => { it('should not insert debug info into contentChildren function if not imported from angular core', () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; declare function contentChildren(value: any): any; const testContentChildren = contentChildren('foo'); @Component({ template: '' }) class MyComponent { testContentChildren = contentChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into contentChildren function if imported from angular core', () => { env.write( 'test.ts', ` import {contentChildren, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChildren = contentChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `contentChildren('foo', ...(ngDevMode ? [{ debugName: "testContentChildren" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {contentChildren, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChildren = contentChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('contentChildren("foo")'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {contentChildren, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChildren = contentChildren('foo'); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `contentChildren("foo", { debugName: "testContentChildren" })`, ); }); it('should tree-shake away debug info if in prod mode with existing options', async () => { env.write( 'test.ts', ` import {contentChildren, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChildren = contentChildren('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('contentChildren("foo", { read: ElementRef })'); }); it('should not tree-shake away debug info if in dev mode with existing options', async () => { env.write( 'test.ts', ` import {contentChildren, Component, ElementRef} from '@angular/core'; @Component({ template: '' }) class MyComponent { testContentChildren = contentChildren('foo', { read: ElementRef }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `contentChildren("foo", { debugName: "testContentChildren", read: ElementRef })`, ); }); }); describe('effect', () => { it('should not insert debug info into effect function if not imported from angular core', () => { env.write( 'test.ts', ` declare function signal(val: any): any; declare function effect(fn: () => any): any; import {Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testEffect = effect(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into effect function if imported from angular core', () => { env.write( 'test.ts', ` import {signal, effect, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testEffect = effect(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `effect(() => this.testSignal(), ...(ngDevMode ? [{ debugName: "testEffect" }] : /* istanbul ignore next */ [])`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, effect, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testEffect = effect(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('effect(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, effect, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testEffect = effect(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `effect(() => this.testSignal(), { debugName: "testEffect" })`, ); }); it('should tree-shake away debug info if in prod mode with existing options', async () => { env.write( 'test.ts', ` import {signal, effect, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testEffect = effect(() => this.testSignal(), { manualCleanup: true, allowSignalWrites: true }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain( 'effect(() => this.testSignal(), { manualCleanup: !0, allowSignalWrites: !0 })', ); }); it('should not tree-shake away debug info if in dev mode with existing options', async () => { env.write( 'test.ts', ` import {signal, effect, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testEffect = effect(() => this.testSignal(), { manualCleanup: true, allowSignalWrites: true }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `effect(() => this.testSignal(), { debugName: "testEffect", manualCleanup: !0, allowSignalWrites: !0 })`, ); }); }); describe('linkedSignal', () => { it('should not insert debug info into linkedSignal function if not imported from angular core', () => { env.write( 'test.ts', ` declare function linkedSignal(fn: () => any): any; const testLinkedSignal = linkedSignal(() => 123); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into linkedSignal function if imported from angular core', () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const testSignal = signal(123); const testLinkedSignal = linkedSignal(() => testSignal()); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `linkedSignal(() => testSignal(), ...(ngDevMode ? [{ debugName: "testLinkedSignal" }] : /* istanbul ignore next */ [])`, ); }); describe('Variable Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const testSignal = signal(123); const testLinkedSignal = linkedSignal(() => testSignal()); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('linkedSignal(() => testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const testSignal = signal(123); const testLinkedSignal = linkedSignal(() => testSignal()); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `linkedSignal(() => testSignal(), { debugName: "testLinkedSignal" })`, ); }); it('should insert debug info into linkedSignal function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const testSignal = signal(123); const testLinkedSignal = linkedSignal(() => testSignal(), { equal: () => true }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `linkedSignal(() => testSignal(), { ...(ngDevMode ? { debugName: "testLinkedSignal" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for linkedSignal function that has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; declare function equal(): boolean; const testSignal = signal(123); const testLinkedSignal = linkedSignal(() => testSignal(), { equal }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain( `testLinkedSignal = linkedSignal(() => testSignal(), { equal })`, ); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; declare function equal(): boolean; const testSignal = signal(123); const testLinkedSignal = linkedSignal(() => testSignal(), { equal }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `testLinkedSignal = linkedSignal(() => testSignal(), { debugName: "testLinkedSignal", equal });`, ); }); it('should insert debug info into linkedSignal with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const testSignal = signal(123); const testLinkedSignal = linkedSignal({ source: testSignal, computation: (src, prev) => src, }); `, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( 'testLinkedSignal = linkedSignal({ ...(ngDevMode ? { debugName: "testLinkedSignal" } : /* istanbul ignore next */ {}), ' + 'source: testSignal, ' + 'computation: (src, prev) => src ' + '})', ); }); it('should tree-shake away debug info if in prod mode for linkedSignal with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const computation = (src: any, prev: any) => src; const testSignal = signal(123); const testLinkedSignal = linkedSignal({ source: testSignal, computation, }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testLinkedSignal = linkedSignal({ source: testSignal, computation })', ); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal} from '@angular/core'; const computation = (src: any, prev: any) => src; const testSignal = signal(123); const testLinkedSignal = linkedSignal({ source: testSignal, computation, }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testLinkedSignal = linkedSignal({ ' + 'debugName: "testLinkedSignal", ' + 'source: testSignal, ' + 'computation ' + '})', ); }); }); describe('Property Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('linkedSignal(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal(() => this.testSignal()); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `linkedSignal(() => this.testSignal(), { debugName: "testLinkedSignal" })`, ); }); it('should insert debug info into linkedSignal function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal(() => this.testSignal(), { equal: () => true }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `linkedSignal(() => this.testSignal(), { ...(ngDevMode ? { debugName: "testLinkedSignal" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for linkedSignal function that has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; const equal = () => true; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal(() => this.testSignal(), { equal }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain( `this.testLinkedSignal = linkedSignal(() => this.testSignal(), { equal })`, ); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; const equal = () => true; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal(() => this.testSignal(), { equal }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `this.testLinkedSignal = linkedSignal(() => this.testSignal(), { debugName: "testLinkedSignal", equal });`, ); }); it('should insert debug info into linkedSignal with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal({ source: this.testSignal, computation: (src, prev) => src, }); } `, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( 'linkedSignal({ ...(ngDevMode ? { debugName: "testLinkedSignal" } : /* istanbul ignore next */ {}), ' + 'source: this.testSignal, ' + 'computation: (src, prev) => src ' + '})', ); }); it('should tree-shake away debug info if in prod mode for linkedSignal with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; const computation = (src: any, prev: any) => src; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal({ source: this.testSignal, computation, }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'this.testLinkedSignal = linkedSignal({ source: this.testSignal, computation })', ); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component} from '@angular/core'; const computation = (src: any, prev: any) => src; @Component({ template: '' }) class MyComponent { testSignal = signal(123); testLinkedSignal = linkedSignal({ source: this.testSignal, computation, }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'this.testLinkedSignal = linkedSignal({ ' + 'debugName: "testLinkedSignal", ' + 'source: this.testSignal, ' + 'computation ' + '})', ); }); }); describe('Property Assignment Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal(() => this.testSignal()); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).not.toContain('debugName'); expect(builtContent).toContain('linkedSignal(() => this.testSignal())'); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal(() => this.testSignal()); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `linkedSignal(() => this.testSignal(), { debugName: "testLinkedSignal" })`, ); }); it('should insert debug info into linkedSignal function that already has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal(() => this.testSignal(), { equal: () => true }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).toContain( `linkedSignal(() => this.testSignal(), { ...(ngDevMode ? { debugName: "testLinkedSignal" } : /* istanbul ignore next */ {}), equal: () => true })`, ); }); it('should tree-shake away debug info if in prod mode for linkedSignal function that has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; const equal = () => true; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal(() => this.testSignal(), { equal }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; expect(builtContent).toContain( `this.testLinkedSignal = linkedSignal(() => this.testSignal(), { equal })`, ); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and has custom options', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; const equal = () => true; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal(() => this.testSignal(), { equal }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; expect(builtContent).toContain( `this.testLinkedSignal = linkedSignal(() => this.testSignal(), { debugName: "testLinkedSignal", equal });`, ); }); it('should insert debug info into linkedSignal with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal({ source: this.testSignal, computation: (src, prev) => src, }); } } `, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( 'linkedSignal({ ...(ngDevMode ? { debugName: "testLinkedSignal" } : /* istanbul ignore next */ {}), ' + 'source: this.testSignal, ' + 'computation: (src, prev) => src ' + '})', ); }); it('should tree-shake away debug info if in prod mode for linkedSignal with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; const computation = (src: any, prev: any) => src; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal({ source: this.testSignal, computation, }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'this.testLinkedSignal = linkedSignal({ source: this.testSignal, computation })', ); expect(builtContent).not.toContain('ngDevMode'); expect(builtContent).not.toContain('debugName'); }); it('should not tree-shake away debug info if in dev mode and with a computation object', async () => { env.write( 'test.ts', ` import {signal, linkedSignal, Component, WritableSignal, Signal} from '@angular/core'; const computation = (src: any, prev: any) => src; @Component({ template: '' }) class MyComponent { testSignal: WritableSignal; testLinkedSignal: Signal; constructor() { this.testSignal = signal(123); this.testLinkedSignal = linkedSignal({ source: this.testSignal, computation, }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'this.testLinkedSignal = linkedSignal({ ' + 'debugName: "testLinkedSignal", ' + 'source: this.testSignal, ' + 'computation ' + '})', ); }); }); }); describe('resource', () => { it('should not insert debug info into resource function if not imported from angular core', () => { env.write( 'test.ts', ` declare function resource(props: any): any; const testResource = resource({ defaultValue: 'foo', loader: async () => 'bar', }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into resource function if imported from angular core', () => { env.write( 'test.ts', ` import {resource} from '@angular/core'; const testResource = resource({ defaultValue: 'foo', loader: async () => 'bar', }); `, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( 'resource({ ' + '...(ngDevMode ? { debugName: "testResource" } : /* istanbul ignore next */ {}), ' + `defaultValue: 'foo', ` + `loader: async () => 'bar' ` + '})', ); }); describe('Variable Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {resource} from '@angular/core'; const loader = async () => 'bar'; const testResource = resource({ defaultValue: "foo", loader, }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); expect(contentWoNewLines).toContain( `testResource = resource({ defaultValue: "foo", loader })`, ); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {resource} from '@angular/core'; const loader = async () => 'bar'; const testResource = resource({ defaultValue: 'foo', loader, }); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testResource = resource({ ' + 'debugName: "testResource", ' + 'defaultValue: "foo", ' + 'loader ' + '})', ); }); }); describe('Property Declaration Case', () => { it('should insert debug info into resource function', () => { env.write( 'test.ts', ` import {resource, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testResource = resource({ defaultValue: 'foo', loader: async () => 'bar', }); }`, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( 'resource({ ' + '...(ngDevMode ? { debugName: "testResource" } : /* istanbul ignore next */ {}), ' + `defaultValue: 'foo', ` + `loader: async () => 'bar' ` + '})', ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {resource, Component} from '@angular/core'; const loader = async () => 'bar'; @Component({ template: '' }) class MyComponent { testResource = resource({ defaultValue: 'foo', loader, }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); expect(contentWoNewLines).toContain( `testResource = resource({ defaultValue: "foo", loader })`, ); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {resource, Component} from '@angular/core'; const loader = async () => 'bar'; @Component({ template: '' }) class MyComponent { testResource = resource({ defaultValue: 'foo', loader, }); } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testResource = resource({ ' + 'debugName: "testResource", ' + 'defaultValue: "foo", ' + 'loader ' + '})', ); }); }); describe('Property Assignment Case', () => { it('should insert debug info into resource function', () => { env.write( 'test.ts', ` import {resource, ResourceRef, Component} from '@angular/core'; @Component({ template: '' }) class MyComponent { testResource: ResourceRef; constructor() { this.testResource = resource({ defaultValue: 'foo', loader: async () => 'bar', }); } } `, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( 'resource({ ' + '...(ngDevMode ? { debugName: "testResource" } : /* istanbul ignore next */ {}), ' + `defaultValue: 'foo', ` + `loader: async () => 'bar' ` + '})', ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {resource, ResourceRef, Component} from '@angular/core'; const loader = async () => 'bar'; @Component({ template: '' }) class MyComponent { testResource: ResourceRef; constructor() { this.testResource = resource({ defaultValue: 'foo', loader, }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); expect(contentWoNewLines).toContain( `testResource = resource({ defaultValue: "foo", loader })`, ); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {resource, ResourceRef, Component} from '@angular/core'; const loader = async () => 'bar'; @Component({ template: '' }) class MyComponent { testResource: ResourceRef; constructor() { this.testResource = resource({ defaultValue: 'foo', loader, }); } } `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testResource = resource({ ' + 'debugName: "testResource", ' + 'defaultValue: "foo", ' + 'loader ' + '})', ); }); }); }); describe('httpResource', () => { it('should not insert debug info into httpResource function if not imported from angular core', () => { env.write( 'test.ts', ` declare function httpResource(props: any): any; const testResource = httpResource(() => '/api'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents).not.toContain('debugName'); }); it('should insert debug info into httpResource function if imported from angular core', () => { env.write( 'test.ts', ` import {httpResource} from '@angular/common/http'; const testHttpResource = httpResource(() => '/api'); `, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( `httpResource(() => '/api', ...(ngDevMode ? [{ debugName: "testHttpResource" }] : /* istanbul ignore next */ []))`, ); }); describe('Variable Declaration Case', () => { it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {httpResource} from '@angular/common/http'; const testHttpResource = httpResource(() => '/api'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api")`); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {httpResource} from '@angular/common/http'; const testHttpResource = httpResource(() => '/api'); `, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testHttpResource = httpResource(() => "/api", { ' + 'debugName: "testHttpResource" ' + '})', ); }); }); describe('Property Declaration Case', () => { it('should insert debug info into httpResource function', () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; import {httpResource} from '@angular/common/http'; @Component({ template: '' }) class MyComponent { testHttpResource = httpResource(() => '/api'); }`, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( `httpResource(() => '/api', ...(ngDevMode ? [{ debugName: "testHttpResource" }] : /* istanbul ignore next */ []))`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; import {httpResource} from '@angular/common/http'; @Component({ template: '' }) class MyComponent { testHttpResource = httpResource(() => '/api'); }`, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api")`); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; import {httpResource} from '@angular/common/http'; @Component({ template: '' }) class MyComponent { testHttpResource = httpResource(() => '/api'); }`, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testHttpResource = httpResource(() => "/api", { ' + 'debugName: "testHttpResource" ' + '})', ); }); }); describe('Property Assignment Case', () => { it('should insert debug info into httpResource function', () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; import {httpResource, HttpResourceRef} from '@angular/common/http'; @Component({ template: '' }) class MyComponent { testHttpResource: HttpResourceRef; constructor() { this.testHttpResource = httpResource(() => '/api'); } }`, ); env.driveMain(); const jsContents = cleanNewLines(env.getContents('test.js')); expect(jsContents).toContain( `httpResource(() => '/api', ...(ngDevMode ? [{ debugName: "testHttpResource" }] : /* istanbul ignore next */ []))`, ); }); it('should tree-shake away debug info if in prod mode', async () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; import {httpResource, HttpResourceRef} from '@angular/common/http'; @Component({ template: '' }) class MyComponent { testHttpResource: HttpResourceRef; constructor() { this.testHttpResource = httpResource(() => '/api'); } }`, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedProdBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).not.toContain('debugName'); expect(contentWoNewLines).toContain(`testHttpResource = httpResource(() => "/api")`); }); it('should not tree-shake away debug info if in dev mode', async () => { env.write( 'test.ts', ` import {Component} from '@angular/core'; import {httpResource, HttpResourceRef} from '@angular/common/http'; @Component({ template: '' }) class MyComponent { testHttpResource: HttpResourceRef; constructor() { this.testHttpResource = httpResource(() => '/api'); } }`, ); env.driveMain(); const jsContents = env.getContents('test.js'); const builtContent = (await esbuild.transform(jsContents, minifiedDevBuildOptions)).code; const contentWoNewLines = cleanNewLines(builtContent); expect(contentWoNewLines).toContain( 'testHttpResource = httpResource(() => "/api", { ' + 'debugName: "testHttpResource" ' + '})', ); }); }); }); }); });