Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ivy): handle rooted resource paths correctly #31511

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 28 additions & 13 deletions packages/compiler-cli/src/ngtsc/resource_loader.ts
Expand Up @@ -7,8 +7,12 @@
*/

import * as ts from 'typescript';

import {CompilerHost} from '../transformers/api';
import {ResourceLoader} from './annotations/src/api';

import {ResourceLoader} from './annotations';
import {AbsoluteFsPath, PathSegment, join} from './file_system';
import {getRootDirs} from './util/src/typescript';

const CSS_PREPROCESSOR_EXT = /(\.scss|\.less|\.styl)$/;

Expand All @@ -19,9 +23,13 @@ export class HostResourceLoader implements ResourceLoader {
private cache = new Map<string, string>();
private fetching = new Map<string, Promise<void>>();

private rootDirs: AbsoluteFsPath[];

canPreload = !!this.host.readResource;

constructor(private host: CompilerHost, private options: ts.CompilerOptions) {}
constructor(private host: CompilerHost, private options: ts.CompilerOptions) {
this.rootDirs = getRootDirs(host, options);
}

/**
* Resolve the url of a resource relative to the file that contains the reference to it.
Expand Down Expand Up @@ -111,19 +119,21 @@ export class HostResourceLoader implements ResourceLoader {
* option from the tsconfig. First, normalize the file name.
*/
private fallbackResolve(url: string, fromFile: string): string|null {
// Strip a leading '/' if one is present.
let candidateLocations: string[];
if (url.startsWith('/')) {
url = url.substr(1);

// Do not take current file location into account if we process absolute path.
fromFile = '';
}
// Turn absolute paths into relative paths.
if (!url.startsWith('.')) {
url = `./${url}`;
// This path is not really an absolute path, but instead the leading '/' means that it's
// rooted in the project rootDirs. So look for it according to the rootDirs.
candidateLocations = this.getRootedCandidateLocations(url);
} else {
// This path is a "relative" path and can be resolved as such. To make this easier on the
// downstream resolver, the './' prefix is added if missing to distinguish these paths from
// absolute node_modules paths.
if (!url.startsWith('.')) {
url = `./${url}`;
}
candidateLocations = this.getResolvedCandidateLocations(url, fromFile);
}

const candidateLocations = this.getCandidateLocations(url, fromFile);
for (const candidate of candidateLocations) {
if (this.host.fileExists(candidate)) {
return candidate;
Expand All @@ -142,6 +152,11 @@ export class HostResourceLoader implements ResourceLoader {
return null;
}

private getRootedCandidateLocations(url: string): AbsoluteFsPath[] {
// The path already starts with '/', so add a '.' to make it relative.
const segment: PathSegment = ('.' + url) as PathSegment;
return this.rootDirs.map(rootDir => join(rootDir, segment));
}

/**
* TypeScript provides utilities to resolve module names, but not resource files (which aren't
Expand All @@ -150,7 +165,7 @@ export class HostResourceLoader implements ResourceLoader {
* a list of file names that were considered, the loader can enumerate the possible locations
* for the file by setting up a module resolution for it that will fail.
*/
private getCandidateLocations(url: string, fromFile: string): string[] {
private getResolvedCandidateLocations(url: string, fromFile: string): string[] {
// `failedLookupLocations` is in the name of the type ts.ResolvedModuleWithFailedLookupLocations
// but is marked @internal in TypeScript. See
// https://github.com/Microsoft/TypeScript/issues/28770.
Expand Down
19 changes: 19 additions & 0 deletions packages/compiler-cli/test/ngtsc/ngtsc_spec.ts
Expand Up @@ -385,6 +385,25 @@ runInEachFileSystem(os => {
expect(jsContents).toContain('Hello World');
});

it('should compile Components with an absolute templateUrl in a different rootDir', () => {
env.tsconfig({}, ['./extraRootDir']);
env.write('extraRootDir/test.html', '<p>Hello World</p>');
env.write('test.ts', `
import {Component} from '@angular/core';

@Component({
selector: 'test-cmp',
templateUrl: '/test.html',
})
export class TestCmp {}
`);

env.driveMain();

const jsContents = env.getContents('test.js');
expect(jsContents).toContain('Hello World');
});

it('should compile components with styleUrls', () => {
env.write('test.ts', `
import {Component} from '@angular/core';
Expand Down