fix(ngcc): support simple `browser` property in entry-points (#36396)
The `browser` package.json property is now supported to the same level as `main` - i.e. it is sniffed for UMD, ESM5 and CommonJS. The `browser` property can also contain an object with file overrides but this is not supported by ngcc. Fixes #36062 PR Close #36396
This commit is contained in:
parent
2463548fa7
commit
6b3aa60446
|
@ -50,6 +50,7 @@ export interface JsonObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PackageJsonFormatPropertiesMap {
|
export interface PackageJsonFormatPropertiesMap {
|
||||||
|
browser?: string;
|
||||||
fesm2015?: string;
|
fesm2015?: string;
|
||||||
fesm5?: string;
|
fesm5?: string;
|
||||||
es2015?: string; // if exists then it is actually FESM2015
|
es2015?: string; // if exists then it is actually FESM2015
|
||||||
|
@ -75,7 +76,7 @@ export interface EntryPointPackageJson extends JsonObject, PackageJsonFormatProp
|
||||||
export type EntryPointJsonProperty = Exclude<PackageJsonFormatProperties, 'types'|'typings'>;
|
export type EntryPointJsonProperty = Exclude<PackageJsonFormatProperties, 'types'|'typings'>;
|
||||||
// We need to keep the elements of this const and the `EntryPointJsonProperty` type in sync.
|
// We need to keep the elements of this const and the `EntryPointJsonProperty` type in sync.
|
||||||
export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
|
export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
|
||||||
['fesm2015', 'fesm5', 'es2015', 'esm2015', 'esm5', 'main', 'module'];
|
['fesm2015', 'fesm5', 'es2015', 'esm2015', 'esm5', 'main', 'module', 'browser'];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,13 +194,18 @@ export function getEntryPointFormat(
|
||||||
return 'esm2015';
|
return 'esm2015';
|
||||||
case 'esm5':
|
case 'esm5':
|
||||||
return 'esm5';
|
return 'esm5';
|
||||||
|
case 'browser':
|
||||||
|
const browserFile = entryPoint.packageJson['browser'];
|
||||||
|
if (typeof browserFile !== 'string') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return sniffModuleFormat(fs, join(entryPoint.path, browserFile));
|
||||||
case 'main':
|
case 'main':
|
||||||
const mainFile = entryPoint.packageJson['main'];
|
const mainFile = entryPoint.packageJson['main'];
|
||||||
if (mainFile === undefined) {
|
if (mainFile === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const pathToMain = join(entryPoint.path, mainFile);
|
return sniffModuleFormat(fs, join(entryPoint.path, mainFile));
|
||||||
return sniffModuleFormat(fs, pathToMain);
|
|
||||||
case 'module':
|
case 'module':
|
||||||
return 'esm5';
|
return 'esm5';
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -793,7 +793,7 @@ runInEachFileSystem(() => {
|
||||||
const propertiesToConsider = ['es1337', 'fesm42'];
|
const propertiesToConsider = ['es1337', 'fesm42'];
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No supported format property to consider among [es1337, fesm42]. Supported ' +
|
'No supported format property to consider among [es1337, fesm42]. Supported ' +
|
||||||
'properties: fesm2015, fesm5, es2015, esm2015, esm5, main, module';
|
'properties: fesm2015, fesm5, es2015, esm2015, esm5, main, module, browser';
|
||||||
|
|
||||||
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider}))
|
expect(() => mainNgcc({basePath: '/node_modules', propertiesToConsider}))
|
||||||
.toThrowError(errorMessage);
|
.toThrowError(errorMessage);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '../../../
|
||||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||||
import {loadTestFiles} from '../../../test/helpers';
|
import {loadTestFiles} from '../../../test/helpers';
|
||||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||||
import {EntryPoint, getEntryPointFormat, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point';
|
import {EntryPoint, EntryPointJsonProperty, getEntryPointFormat, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT, SUPPORTED_FORMAT_PROPERTIES} from '../../src/packages/entry_point';
|
||||||
import {MockLogger} from '../helpers/mock_logger';
|
import {MockLogger} from '../helpers/mock_logger';
|
||||||
|
|
||||||
runInEachFileSystem(() => {
|
runInEachFileSystem(() => {
|
||||||
|
@ -206,7 +206,7 @@ runInEachFileSystem(() => {
|
||||||
|
|
||||||
for (let prop of SUPPORTED_FORMAT_PROPERTIES) {
|
for (let prop of SUPPORTED_FORMAT_PROPERTIES) {
|
||||||
// Ignore the UMD format
|
// Ignore the UMD format
|
||||||
if (prop === 'main') continue;
|
if (prop === 'main' || prop === 'browser') continue;
|
||||||
// Let's give 'module' a specific path, otherwise compute it based on the property.
|
// Let's give 'module' a specific path, otherwise compute it based on the property.
|
||||||
const typingsPath = prop === 'module' ? 'index' : `${prop}/missing_typings`;
|
const typingsPath = prop === 'module' ? 'index' : `${prop}/missing_typings`;
|
||||||
|
|
||||||
|
@ -425,67 +425,80 @@ runInEachFileSystem(() => {
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm5');
|
expect(getEntryPointFormat(fs, entryPoint, 'module')).toBe('esm5');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return `esm5` for `main` if the file contains import or export statements', () => {
|
(['browser', 'main'] as EntryPointJsonProperty[]).forEach(browserOrMain => {
|
||||||
const name = _(
|
it('should return `esm5` for `' + browserOrMain +
|
||||||
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js');
|
'` if the file contains import or export statements',
|
||||||
loadTestFiles([{name, contents: `import * as core from '@angular/core;`}]);
|
() => {
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('esm5');
|
const name = _(
|
||||||
|
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js');
|
||||||
|
loadTestFiles([{name, contents: `import * as core from '@angular/core;`}]);
|
||||||
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
|
||||||
|
|
||||||
loadTestFiles([{name, contents: `import {Component} from '@angular/core;`}]);
|
loadTestFiles([{name, contents: `import {Component} from '@angular/core;`}]);
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('esm5');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
|
||||||
|
|
||||||
loadTestFiles([{name, contents: `export function foo() {}`}]);
|
loadTestFiles([{name, contents: `export function foo() {}`}]);
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('esm5');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
|
||||||
|
|
||||||
loadTestFiles([{name, contents: `export * from 'abc';`}]);
|
loadTestFiles([{name, contents: `export * from 'abc';`}]);
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('esm5');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('esm5');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return `umd` for `main` if the file contains a UMD wrapper function', () => {
|
it('should return `umd` for `' + browserOrMain +
|
||||||
loadTestFiles([{
|
'` if the file contains a UMD wrapper function',
|
||||||
name: _(
|
() => {
|
||||||
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
loadTestFiles([{
|
||||||
contents: `
|
name: _(
|
||||||
|
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
||||||
|
contents: `
|
||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
|
||||||
typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
|
typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
|
||||||
(global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
|
(global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
|
||||||
}(this, function (exports, core) { 'use strict'; }));
|
}(this, function (exports, core) { 'use strict'; }));
|
||||||
`
|
`
|
||||||
}]);
|
}]);
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('umd');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return `commonjs` for `main` if the file does not contain a UMD wrapper function', () => {
|
it('should return `commonjs` for `' + browserOrMain +
|
||||||
loadTestFiles([{
|
'` if the file does not contain a UMD wrapper function',
|
||||||
name: _(
|
() => {
|
||||||
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
loadTestFiles([{
|
||||||
contents: `
|
name: _(
|
||||||
|
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
||||||
|
contents: `
|
||||||
const core = require('@angular/core);
|
const core = require('@angular/core);
|
||||||
module.exports = {};
|
module.exports = {};
|
||||||
`
|
`
|
||||||
}]);
|
}]);
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('commonjs');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('commonjs');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve the format path with suitable postfixes', () => {
|
it('should resolve the format path with suitable postfixes', () => {
|
||||||
loadTestFiles([{
|
loadTestFiles([{
|
||||||
name: _(
|
name: _(
|
||||||
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
'/project/node_modules/some_package/valid_entry_point/bundles/valid_entry_point/index.js'),
|
||||||
contents: `
|
contents: `
|
||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core')) :
|
||||||
typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
|
typeof define === 'function' && define.amd ? define('@angular/common', ['exports', '@angular/core'], factory) :
|
||||||
(global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
|
(global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core));
|
||||||
}(this, function (exports, core) { 'use strict'; }));
|
}(this, function (exports, core) { 'use strict'; }));
|
||||||
`
|
`
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
entryPoint.packageJson.main = './bundles/valid_entry_point/index';
|
entryPoint.packageJson.main = './bundles/valid_entry_point/index';
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('umd');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
|
||||||
|
|
||||||
entryPoint.packageJson.main = './bundles/valid_entry_point';
|
entryPoint.packageJson.main = './bundles/valid_entry_point';
|
||||||
expect(getEntryPointFormat(fs, entryPoint, 'main')).toBe('umd');
|
expect(getEntryPointFormat(fs, entryPoint, browserOrMain)).toBe('umd');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return `undefined` if the `browser` property is not a string', () => {
|
||||||
|
entryPoint.packageJson.browser = {} as any;
|
||||||
|
expect(getEntryPointFormat(fs, entryPoint, 'browser')).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -503,6 +516,7 @@ export function createPackageJson(
|
||||||
fesm5: `./fesm5/${packageName}.js`,
|
fesm5: `./fesm5/${packageName}.js`,
|
||||||
esm5: `./esm5/${packageName}.js`,
|
esm5: `./esm5/${packageName}.js`,
|
||||||
main: `./bundles/${packageName}/index.js`,
|
main: `./bundles/${packageName}/index.js`,
|
||||||
|
browser: `./bundles/${packageName}/index.js`,
|
||||||
module: './index.js',
|
module: './index.js',
|
||||||
};
|
};
|
||||||
if (excludes) {
|
if (excludes) {
|
||||||
|
|
Loading…
Reference in New Issue