Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions packages/angular/build/src/tools/angular/angular-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ export function createAngularCompilerHost(
// The AOT compiler currently requires this hook to allow for a transformResource hook.
// Once the AOT compiler allows only a transformResource hook, this can be reevaluated.
host.readResource = async function (filename) {
// Only template resources need to be read by the compiler host.
// External stylesheets are read and bundled by the stylesheet bundler during `transformResource`.
if (!hasTemplateExtension(filename)) {
return '';
}

return this.readFile(filename) ?? '';
};

Expand All @@ -197,8 +203,8 @@ export function createAngularCompilerHost(
'External runtime stylesheets should not be transformed: ' + context.resourceFile,
);

// No transformation required if the resource is empty
if (data.trim().length === 0) {
// No transformation required if an inline resource is empty
if (!context.resourceFile && data.trim().length === 0) {
return { content: '' };
}

Expand Down
97 changes: 97 additions & 0 deletions packages/angular/build/src/tools/angular/angular-host_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @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 ts from 'typescript';
import { type AngularHostOptions, createAngularCompilerHost } from './angular-host';

describe('createAngularCompilerHost', () => {
function createHost() {
const transformStylesheet = jasmine
.createSpy<AngularHostOptions['transformStylesheet']>('transformStylesheet')
.and.resolveTo('.compiled {}');
const processWebWorker = jasmine
.createSpy<AngularHostOptions['processWebWorker']>('processWebWorker')
.and.returnValue('worker.js');

const host = createAngularCompilerHost(
ts,
{},
{
transformStylesheet,
processWebWorker,
},
undefined,
);

return { host, transformStylesheet };
}

describe('readResource', () => {
it('should not call readFile for .css and .scss stylesheet files', async () => {
const { host } = createHost();
const readFileSpy = spyOn(host, 'readFile').and.returnValue('.btn { color: red; }');

expect(await host.readResource?.('/app/src/app.component.css')).toBe('');
expect(await host.readResource?.('/app/src/app.component.scss')).toBe('');
expect(readFileSpy).not.toHaveBeenCalled();
});

it('should call readFile for .html and .svg template files', async () => {
const { host } = createHost();
const readFileSpy = spyOn(host, 'readFile').and.callFake(
(fileName) => `<content of ${fileName}>`,
);

expect(await host.readResource?.('/app/src/app.component.html')).toBe(
'<content of /app/src/app.component.html>',
);
expect(await host.readResource?.('/app/src/icon.component.svg')).toBe(
'<content of /app/src/icon.component.svg>',
);
expect(readFileSpy).toHaveBeenCalledTimes(2);
});
});

describe('transformResource', () => {
it('should invoke transformStylesheet when context.resourceFile is set even if data is empty', async () => {
const { host, transformStylesheet } = createHost();

const result = await host.transformResource?.('', {
type: 'style',
containingFile: '/app/src/app.component.ts',
resourceFile: '/app/src/app.component.scss',
order: 0,
className: 'AppComponent',
});

expect(result).toEqual({ content: '.compiled {}' });
expect(transformStylesheet).toHaveBeenCalledOnceWith(
'',
'/app/src/app.component.ts',
'/app/src/app.component.scss',
0,
'AppComponent',
);
});

it('should short-circuit empty inline styles without calling transformStylesheet', async () => {
const { host, transformStylesheet } = createHost();

const result = await host.transformResource?.(' ', {
type: 'style',
containingFile: '/app/src/app.component.ts',
resourceFile: null,
order: 0,
className: 'AppComponent',
});

expect(result).toEqual({ content: '' });
expect(transformStylesheet).not.toHaveBeenCalled();
});
});
});
Loading