Skip to content

Commit fca058a

Browse files
devversionthePunderWoman
authored andcommitted
refactor(migrations): ensure tsurge can properly emit references in g3 (#62447)
Currently when Tsurge runs in g3, it creates a bare bones Angular compiler plugin. The tsconfigs from compilation units may set options like "useHostForImportGeneration", but the Ngtsc logic doesn't enable because the `fileNameToModuleName` method is not defined on the host. This can break reference emission for Tsurge analyzers/programs and result in subtle differences to real `ng_module` compilations. This commit fixes this by making the method available in 1P Tsurge. Notably, reference emission can occur during analysis— so even if migrations aren't "emitting TS -> JS" output. PR Close #62447
1 parent cea42d0 commit fca058a

File tree

4 files changed

+76
-17
lines changed

4 files changed

+76
-17
lines changed

packages/core/schematics/utils/tsurge/helpers/create_program.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {NgCompilerOptions} from '@angular/compiler-cli/src/ngtsc/core/api';
9+
import {NgCompilerOptions, UnifiedModulesHost} from '@angular/compiler-cli/src/ngtsc/core/api';
1010
import {
1111
absoluteFrom,
1212
FileSystem,
1313
NgtscCompilerHost,
14-
NodeJSFileSystem,
1514
setFileSystem,
1615
} from '@angular/compiler-cli/src/ngtsc/file_system';
1716
import {isShim} from '@angular/compiler-cli/src/ngtsc/shims';
1817
import {getRootDirs} from '@angular/compiler-cli/src/ngtsc/util/src/typescript';
1918
import {BaseProgramInfo, ProgramInfo} from '../program_info';
20-
import {google3UsePlainTsProgramIfNoKnownAngularOption} from './google3/target_detection';
19+
import {google3UsePlainTsProgramIfNoKnownAngularOption, isGoogle3} from './google3/detection';
2120
import {createNgtscProgram} from './ngtsc_program';
2221
import {parseTsconfigOrDie} from './ts_parse_config';
2322
import {createPlainTsProgram} from './ts_program';
23+
import {fileNameToModuleNameFactory} from './google3/unified_module_resolution';
2424

2525
/** Creates the base program info for the given tsconfig path. */
2626
export function createBaseProgramInfo(
@@ -45,6 +45,16 @@ export function createBaseProgramInfo(
4545
return createPlainTsProgram(tsHost, tsconfig, optionOverrides);
4646
}
4747

48+
// The Angular program may try to emit references during analysis or migration.
49+
// To replicate the Google3 import emission here, ensure the unified module resolution
50+
// can be enabled by the compiler.
51+
if (isGoogle3() && tsconfig.options.rootDirs) {
52+
(tsHost as Partial<UnifiedModulesHost>).fileNameToModuleName = fileNameToModuleNameFactory(
53+
tsconfig.options.rootDirs,
54+
/* workspaceName*/ 'google3',
55+
);
56+
}
57+
4858
return createNgtscProgram(tsHost, tsconfig, optionOverrides);
4959
}
5060

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
/** Whether we are executing inside Google */
10+
export function isGoogle3() {
11+
return process.env['GOOGLE3_TSURGE'] === '1';
12+
}
13+
14+
/**
15+
* By default, Tsurge will always create an Angular compiler program
16+
* for projects analyzed and migrated. This works perfectly fine in
17+
* third-party where Tsurge migrations run in Angular CLI projects.
18+
*
19+
* In first party, when running against full Google3, creating an Angular
20+
* program for e.g. plain `ts_library` targets is overly expensive and
21+
* can result in out of memory issues for large TS targets. In 1P we can
22+
* reliably distinguish between TS and Angular targets via the `angularCompilerOptions`.
23+
*/
24+
export function google3UsePlainTsProgramIfNoKnownAngularOption() {
25+
return process.env['GOOGLE3_TSURGE'] === '1';
26+
}

packages/core/schematics/utils/tsurge/helpers/google3/target_detection.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,3 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.dev/license
77
*/
8-
9-
/**
10-
* By default, Tsurge will always create an Angular compiler program
11-
* for projects analyzed and migrated. This works perfectly fine in
12-
* third-party where Tsurge migrations run in Angular CLI projects.
13-
*
14-
* In first party, when running against full Google3, creating an Angular
15-
* program for e.g. plain `ts_library` targets is overly expensive and
16-
* can result in out of memory issues for large TS targets. In 1P we can
17-
* reliably distinguish between TS and Angular targets via the `angularCompilerOptions`.
18-
*/
19-
export function google3UsePlainTsProgramIfNoKnownAngularOption() {
20-
return process.env['GOOGLE3_TSURGE'] === '1';
21-
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
// Note: Try to keep mostly in sync with
10+
// //depot/google3/javascript/angular2/tools/ngc_wrapped/tsc_plugin.ts
11+
// TODO: Consider moving this logic into the 1P launcher.
12+
13+
import * as path from 'node:path';
14+
15+
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
16+
17+
export function fileNameToModuleNameFactory(
18+
rootDirs: readonly string[],
19+
workspaceName: string,
20+
): (importedFilePath: string) => string {
21+
return (importedFilePath: string) => {
22+
let relativePath = '';
23+
for (const rootDir of rootDirs) {
24+
const rel = path.posix.relative(rootDir, importedFilePath);
25+
if (!rel.startsWith('.')) {
26+
relativePath = rel;
27+
break;
28+
}
29+
}
30+
31+
if (relativePath) {
32+
return `${workspaceName}/${relativePath.replace(EXT, '')}`;
33+
} else {
34+
return importedFilePath.replace(EXT, '');
35+
}
36+
};
37+
}

0 commit comments

Comments
 (0)