fix(ngcc): use path-mappings from tsconfig in dependency resolution (#36180)
When computing the dependencies between packages which are not in node_modules, we may need to rely upon path-mappings to find the path to the imported entry-point. This commit allows ngcc to use the path-mappings from a tsconfig file to find dependencies. By default any tsconfig.json file in the directory above the `basePath` is loaded but it is possible to use a path to a specific file by providing the `tsConfigPath` property to mainNgcc, or to turn off loading any tsconfig file by setting `tsConfigPath` to `null`. At the command line this is controlled via the `--tsconfig` option. Fixes #36119 PR Close #36180
This commit is contained in:

committed by
Misko Hevery

parent
4f9717331d
commit
380de1e7b4
@ -1230,22 +1230,134 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
|
||||
describe('with pathMappings', () => {
|
||||
it('should find and compile packages accessible via the pathMappings', () => {
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
propertiesToConsider: ['es2015'],
|
||||
pathMappings: {paths: {'*': ['dist/*']}, baseUrl: '/'},
|
||||
});
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||
it('should infer the @app pathMapping from a local tsconfig.json path', () => {
|
||||
fs.writeFile(
|
||||
_('/tsconfig.json'),
|
||||
JSON.stringify({compilerOptions: {paths: {'@app/*': ['dist/*']}, baseUrl: './'}}));
|
||||
const logger = new MockLogger();
|
||||
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015'], logger});
|
||||
expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
// The local-package-3 and local-package-4 will not be processed because there is no path
|
||||
// mappings for `@x` and plain local imports.
|
||||
expect(loadPackage('local-package-3', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-3')}.`,
|
||||
'It is missing required dependencies:\n - @x/local-package'
|
||||
]);
|
||||
expect(loadPackage('local-package-4', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-4')}.`,
|
||||
'It is missing required dependencies:\n - local-package'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should read the @x pathMapping from a specified tsconfig.json path', () => {
|
||||
fs.writeFile(
|
||||
_('/tsconfig.app.json'),
|
||||
JSON.stringify({compilerOptions: {paths: {'@x/*': ['dist/*']}, baseUrl: './'}}));
|
||||
const logger = new MockLogger();
|
||||
mainNgcc({
|
||||
basePath: '/dist',
|
||||
propertiesToConsider: ['es2015'],
|
||||
tsConfigPath: _('/tsconfig.app.json'), logger
|
||||
});
|
||||
expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('local-package-3', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
// The local-package-2 and local-package-4 will not be processed because there is no path
|
||||
// mappings for `@app` and plain local imports.
|
||||
expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-2')}.`,
|
||||
'It is missing required dependencies:\n - @app/local-package'
|
||||
]);
|
||||
expect(loadPackage('local-package-4', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-4')}.`,
|
||||
'It is missing required dependencies:\n - local-package'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should use the explicit `pathMappings`, ignoring the local tsconfig.json settings',
|
||||
() => {
|
||||
const logger = new MockLogger();
|
||||
fs.writeFile(
|
||||
_('/tsconfig.json'),
|
||||
JSON.stringify({compilerOptions: {paths: {'@app/*': ['dist/*']}, baseUrl: './'}}));
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
propertiesToConsider: ['es2015'],
|
||||
pathMappings: {paths: {'*': ['dist/*']}, baseUrl: '/'}, logger
|
||||
});
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
expect(loadPackage('local-package-4', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
// The local-package-2 and local-package-3 will not be processed because there is no path
|
||||
// mappings for `@app` and `@x` local imports.
|
||||
expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-2')}.`,
|
||||
'It is missing required dependencies:\n - @app/local-package'
|
||||
]);
|
||||
expect(loadPackage('local-package-3', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-3')}.`,
|
||||
'It is missing required dependencies:\n - @x/local-package'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not use pathMappings from a local tsconfig.json path if tsConfigPath is null',
|
||||
() => {
|
||||
const logger = new MockLogger();
|
||||
fs.writeFile(
|
||||
_('/tsconfig.json'),
|
||||
JSON.stringify({compilerOptions: {paths: {'@app/*': ['dist/*']}, baseUrl: './'}}));
|
||||
mainNgcc({
|
||||
basePath: '/dist',
|
||||
propertiesToConsider: ['es2015'],
|
||||
tsConfigPath: null, logger,
|
||||
});
|
||||
expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
});
|
||||
// Since the tsconfig is not loaded, the `@app/local-package` import in `local-package-2`
|
||||
// is not path-mapped correctly, and so it fails to be processed.
|
||||
expect(loadPackage('local-package-2', _('/dist')).__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
expect(logger.logs.debug).toContain([
|
||||
`Invalid entry-point ${_('/dist/local-package-2')}.`,
|
||||
'It is missing required dependencies:\n - @app/local-package'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with configuration files', () => {
|
||||
@ -1748,7 +1860,7 @@ runInEachFileSystem(() => {
|
||||
},
|
||||
]);
|
||||
|
||||
// An Angular package that has been built locally and stored in the `dist` directory.
|
||||
// Angular packages that have been built locally and stored in the `dist` directory.
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/dist/local-package/package.json'),
|
||||
@ -1764,6 +1876,54 @@ runInEachFileSystem(() => {
|
||||
name: _('/dist/local-package/index.d.ts'),
|
||||
contents: `export declare class AppComponent {};`
|
||||
},
|
||||
// local-package-2 depends upon local-package, via an `@app` aliased import.
|
||||
{
|
||||
name: _('/dist/local-package-2/package.json'),
|
||||
contents: '{"name": "local-package-2", "es2015": "./index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{name: _('/dist/local-package-2/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/dist/local-package-2/index.js'),
|
||||
contents:
|
||||
`import {Component} from '@angular/core';\nexport {AppComponent} from '@app/local-package';`
|
||||
},
|
||||
{
|
||||
name: _('/dist/local-package-2/index.d.ts'),
|
||||
contents:
|
||||
`import {Component} from '@angular/core';\nexport {AppComponent} from '@app/local-package';`
|
||||
},
|
||||
// local-package-3 depends upon local-package, via an `@x` aliased import.
|
||||
{
|
||||
name: _('/dist/local-package-3/package.json'),
|
||||
contents: '{"name": "local-package-3", "es2015": "./index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{name: _('/dist/local-package-3/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/dist/local-package-3/index.js'),
|
||||
contents:
|
||||
`import {Component} from '@angular/core';\nexport {AppComponent} from '@x/local-package';`
|
||||
},
|
||||
{
|
||||
name: _('/dist/local-package-3/index.d.ts'),
|
||||
contents:
|
||||
`import {Component} from '@angular/core';\nexport {AppComponent} from '@x/local-package';`
|
||||
},
|
||||
// local-package-4 depends upon local-package, via a plain import.
|
||||
{
|
||||
name: _('/dist/local-package-4/package.json'),
|
||||
contents: '{"name": "local-package-", "es2015": "./index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{name: _('/dist/local-package-4/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/dist/local-package-4/index.js'),
|
||||
contents:
|
||||
`import {Component} from '@angular/core';\nexport {AppComponent} from 'local-package';`
|
||||
},
|
||||
{
|
||||
name: _('/dist/local-package-4/index.d.ts'),
|
||||
contents:
|
||||
`import {Component} from '@angular/core';\nexport {AppComponent} from 'local-package';`
|
||||
},
|
||||
]);
|
||||
|
||||
// An Angular package that has a missing dependency
|
||||
|
Reference in New Issue
Block a user