feat(ivy): customize ngcc via configuration files (#30591)
There are scenarios where it is not possible for ngcc to guess the format or configuration of an entry-point just from the files on disk. Such scenarios include: 1) Unwanted entry-points: A spurious package.json makes ngcc think there is an entry-point when there should not be one. 2) Deep-import entry-points: some packages allow deep-imports but do not provide package.json files to indicate to ngcc that the imported path is actually an entry-point to be processed. 3) Invalid/missing package.json properties: For example, an entry-point that does not provide a valid property to a required format. The configuration is provided by one or more `ngcc.config.js` files: * If placed at the root of the project, this file can provide configuration for named packages (and their entry-points) that have been npm installed into the project. * If published as part of a package, the file can provide configuration for entry-points of the package. The configured of a package at the project level will override any configuration provided by the package itself. PR Close #30591
This commit is contained in:
parent
4004d15ba5
commit
7c4c676413
|
@ -5,7 +5,7 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, resolve} from '../../src/ngtsc/file_system';
|
import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system';
|
||||||
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
|
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
|
||||||
import {DependencyResolver} from './dependencies/dependency_resolver';
|
import {DependencyResolver} from './dependencies/dependency_resolver';
|
||||||
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
||||||
|
@ -14,6 +14,7 @@ import {UmdDependencyHost} from './dependencies/umd_dependency_host';
|
||||||
import {ConsoleLogger, LogLevel} from './logging/console_logger';
|
import {ConsoleLogger, LogLevel} from './logging/console_logger';
|
||||||
import {Logger} from './logging/logger';
|
import {Logger} from './logging/logger';
|
||||||
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
|
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
|
||||||
|
import {NgccConfiguration} from './packages/configuration';
|
||||||
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
|
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
|
||||||
import {makeEntryPointBundle} from './packages/entry_point_bundle';
|
import {makeEntryPointBundle} from './packages/entry_point_bundle';
|
||||||
import {EntryPointFinder} from './packages/entry_point_finder';
|
import {EntryPointFinder} from './packages/entry_point_finder';
|
||||||
|
@ -92,7 +93,8 @@ export function mainNgcc(
|
||||||
umd: umdDependencyHost,
|
umd: umdDependencyHost,
|
||||||
commonjs: commonJsDependencyHost
|
commonjs: commonJsDependencyHost
|
||||||
});
|
});
|
||||||
const finder = new EntryPointFinder(fileSystem, logger, resolver);
|
const config = new NgccConfiguration(fileSystem, dirname(absoluteFrom(basePath)));
|
||||||
|
const finder = new EntryPointFinder(fileSystem, config, logger, resolver);
|
||||||
const fileWriter = getFileWriter(fileSystem, createNewEntryPointFormats);
|
const fileWriter = getFileWriter(fileSystem, createNewEntryPointFormats);
|
||||||
|
|
||||||
const absoluteTargetEntryPointPath =
|
const absoluteTargetEntryPointPath =
|
||||||
|
@ -192,6 +194,10 @@ function hasProcessedTargetEntryPoint(
|
||||||
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
|
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
|
||||||
compileAllFormats: boolean) {
|
compileAllFormats: boolean) {
|
||||||
const packageJsonPath = resolve(targetPath, 'package.json');
|
const packageJsonPath = resolve(targetPath, 'package.json');
|
||||||
|
// It might be that this target is configured in which case its package.json might not exist.
|
||||||
|
if (!fs.exists(packageJsonPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
||||||
|
|
||||||
for (const property of propertiesToConsider) {
|
for (const property of propertiesToConsider) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system';
|
import {AbsoluteFsPath, FileSystem, dirname} from '../../../src/ngtsc/file_system';
|
||||||
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
|
import {EntryPointJsonProperty, EntryPointPackageJson} from './entry_point';
|
||||||
|
|
||||||
export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
|
export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
|
||||||
|
@ -49,5 +49,8 @@ export function markAsProcessed(
|
||||||
format: EntryPointJsonProperty) {
|
format: EntryPointJsonProperty) {
|
||||||
if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
|
if (!packageJson.__processed_by_ivy_ngcc__) packageJson.__processed_by_ivy_ngcc__ = {};
|
||||||
packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
|
packageJson.__processed_by_ivy_ngcc__[format] = NGCC_VERSION;
|
||||||
|
// Just in case this package.json was synthesized due to a custom configuration
|
||||||
|
// we will ensure that the path to the containing folder exists before we write the file.
|
||||||
|
fs.ensureDir(dirname(packageJsonPath));
|
||||||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. 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.io/license
|
||||||
|
*/
|
||||||
|
import * as vm from 'vm';
|
||||||
|
import {AbsoluteFsPath, FileSystem, dirname, join, resolve} from '../../../src/ngtsc/file_system';
|
||||||
|
import {PackageJsonFormatProperties} from './entry_point';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format of a project level configuration file.
|
||||||
|
*/
|
||||||
|
export interface NgccProjectConfig { packages: {[packagePath: string]: NgccPackageConfig}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format of a package level configuration file.
|
||||||
|
*/
|
||||||
|
export interface NgccPackageConfig {
|
||||||
|
/**
|
||||||
|
* The entry-points to configure for this package.
|
||||||
|
*
|
||||||
|
* In the config file the keys can be paths relative to the package path;
|
||||||
|
* but when being read back from the `NgccConfiguration` service, these paths
|
||||||
|
* will be absolute.
|
||||||
|
*/
|
||||||
|
entryPoints: {[entryPointPath: string]: NgccEntryPointConfig;};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options for an entry-point.
|
||||||
|
*
|
||||||
|
* The existence of a configuration for a path tells ngcc that this should be considered for
|
||||||
|
* processing as an entry-point.
|
||||||
|
*/
|
||||||
|
export interface NgccEntryPointConfig {
|
||||||
|
/** Do not process (or even acknowledge the existence of) this entry-point, if true. */
|
||||||
|
ignore?: boolean;
|
||||||
|
/**
|
||||||
|
* This property, if provided, holds values that will override equivalent properties in an
|
||||||
|
* entry-point's package.json file.
|
||||||
|
*/
|
||||||
|
override?: PackageJsonFormatProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NGCC_CONFIG_FILENAME = 'ngcc.config.js';
|
||||||
|
|
||||||
|
export class NgccConfiguration {
|
||||||
|
// TODO: change string => ModuleSpecifier when we tighten the path types in #30556
|
||||||
|
private cache = new Map<string, NgccPackageConfig>();
|
||||||
|
|
||||||
|
constructor(private fs: FileSystem, baseDir: AbsoluteFsPath) {
|
||||||
|
const projectConfig = this.loadProjectConfig(baseDir);
|
||||||
|
for (const packagePath in projectConfig.packages) {
|
||||||
|
const absPackagePath = resolve(baseDir, 'node_modules', packagePath);
|
||||||
|
const packageConfig = projectConfig.packages[packagePath];
|
||||||
|
packageConfig.entryPoints =
|
||||||
|
this.processEntryPoints(absPackagePath, packageConfig.entryPoints);
|
||||||
|
this.cache.set(absPackagePath, packageConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfig(packagePath: AbsoluteFsPath): NgccPackageConfig {
|
||||||
|
if (this.cache.has(packagePath)) {
|
||||||
|
return this.cache.get(packagePath) !;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageConfig = this.loadPackageConfig(packagePath);
|
||||||
|
packageConfig.entryPoints = this.processEntryPoints(packagePath, packageConfig.entryPoints);
|
||||||
|
this.cache.set(packagePath, packageConfig);
|
||||||
|
return packageConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadProjectConfig(baseDir: AbsoluteFsPath): NgccProjectConfig {
|
||||||
|
const configFilePath = join(baseDir, NGCC_CONFIG_FILENAME);
|
||||||
|
if (this.fs.exists(configFilePath)) {
|
||||||
|
try {
|
||||||
|
return this.evalSrcFile(configFilePath);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Invalid project configuration file at "${configFilePath}": ` + e.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {packages: {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadPackageConfig(packagePath: AbsoluteFsPath): NgccPackageConfig {
|
||||||
|
const configFilePath = join(packagePath, NGCC_CONFIG_FILENAME);
|
||||||
|
if (this.fs.exists(configFilePath)) {
|
||||||
|
try {
|
||||||
|
return this.evalSrcFile(configFilePath);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Invalid package configuration file at "${configFilePath}": ` + e.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {entryPoints: {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private evalSrcFile(srcPath: AbsoluteFsPath): any {
|
||||||
|
const src = this.fs.readFile(srcPath);
|
||||||
|
const theExports = {};
|
||||||
|
const sandbox = {
|
||||||
|
module: {exports: theExports},
|
||||||
|
exports: theExports, require,
|
||||||
|
__dirname: dirname(srcPath),
|
||||||
|
__filename: srcPath
|
||||||
|
};
|
||||||
|
vm.runInNewContext(src, sandbox, {filename: srcPath});
|
||||||
|
return sandbox.module.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
private processEntryPoints(
|
||||||
|
packagePath: AbsoluteFsPath, entryPoints: {[entryPointPath: string]: NgccEntryPointConfig;}):
|
||||||
|
{[entryPointPath: string]: NgccEntryPointConfig;} {
|
||||||
|
const processedEntryPoints: {[entryPointPath: string]: NgccEntryPointConfig;} = {};
|
||||||
|
for (const entryPointPath in entryPoints) {
|
||||||
|
// Change the keys to be absolute paths
|
||||||
|
processedEntryPoints[resolve(packagePath, entryPointPath)] = entryPoints[entryPointPath];
|
||||||
|
}
|
||||||
|
return processedEntryPoints;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,13 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {relative} from 'canonical-path';
|
||||||
|
import {basename} from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import {AbsoluteFsPath, FileSystem, join, resolve} from '../../../src/ngtsc/file_system';
|
import {AbsoluteFsPath, FileSystem, join, resolve} from '../../../src/ngtsc/file_system';
|
||||||
import {parseStatementForUmdModule} from '../host/umd_host';
|
import {parseStatementForUmdModule} from '../host/umd_host';
|
||||||
import {Logger} from '../logging/logger';
|
import {Logger} from '../logging/logger';
|
||||||
|
import {NgccConfiguration, NgccEntryPointConfig} from './configuration';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The possible values for the format of an entry-point.
|
* The possible values for the format of an entry-point.
|
||||||
|
@ -34,7 +37,7 @@ export interface EntryPoint {
|
||||||
compiledByAngular: boolean;
|
compiledByAngular: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PackageJsonFormatProperties {
|
export interface PackageJsonFormatProperties {
|
||||||
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
|
||||||
|
@ -67,18 +70,25 @@ export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
|
||||||
* @returns An entry-point if it is valid, `null` otherwise.
|
* @returns An entry-point if it is valid, `null` otherwise.
|
||||||
*/
|
*/
|
||||||
export function getEntryPointInfo(
|
export function getEntryPointInfo(
|
||||||
fs: FileSystem, logger: Logger, packagePath: AbsoluteFsPath,
|
fs: FileSystem, config: NgccConfiguration, logger: Logger, packagePath: AbsoluteFsPath,
|
||||||
entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||||
const packageJsonPath = resolve(entryPointPath, 'package.json');
|
const packageJsonPath = resolve(entryPointPath, 'package.json');
|
||||||
if (!fs.exists(packageJsonPath)) {
|
const entryPointConfig = config.getConfig(packagePath).entryPoints[entryPointPath];
|
||||||
|
if (entryPointConfig === undefined && !fs.exists(packageJsonPath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entryPointPackageJson = loadEntryPointPackage(fs, logger, packageJsonPath);
|
if (entryPointConfig !== undefined && entryPointConfig.ignore === true) {
|
||||||
if (!entryPointPackageJson) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadedEntryPointPackageJson =
|
||||||
|
loadEntryPointPackage(fs, logger, packageJsonPath, entryPointConfig !== undefined);
|
||||||
|
const entryPointPackageJson = mergeConfigAndPackageJson(
|
||||||
|
loadedEntryPointPackageJson, entryPointConfig, packagePath, entryPointPath);
|
||||||
|
if (entryPointPackageJson === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// We must have a typings property
|
// We must have a typings property
|
||||||
const typings = entryPointPackageJson.typings || entryPointPackageJson.types;
|
const typings = entryPointPackageJson.typings || entryPointPackageJson.types;
|
||||||
|
@ -86,16 +96,18 @@ export function getEntryPointInfo(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also there must exist a `metadata.json` file next to the typings entry-point.
|
// An entry-point is assumed to be compiled by Angular if there is either:
|
||||||
|
// * a `metadata.json` file next to the typings entry-point
|
||||||
|
// * a custom config for this entry-point
|
||||||
const metadataPath = resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
|
const metadataPath = resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json');
|
||||||
|
const compiledByAngular = entryPointConfig !== undefined || fs.exists(metadataPath);
|
||||||
|
|
||||||
const entryPointInfo: EntryPoint = {
|
const entryPointInfo: EntryPoint = {
|
||||||
name: entryPointPackageJson.name,
|
name: entryPointPackageJson.name,
|
||||||
packageJson: entryPointPackageJson,
|
packageJson: entryPointPackageJson,
|
||||||
package: packagePath,
|
package: packagePath,
|
||||||
path: entryPointPath,
|
path: entryPointPath,
|
||||||
typings: resolve(entryPointPath, typings),
|
typings: resolve(entryPointPath, typings), compiledByAngular,
|
||||||
compiledByAngular: fs.exists(metadataPath),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return entryPointInfo;
|
return entryPointInfo;
|
||||||
|
@ -140,12 +152,15 @@ export function getEntryPointFormat(
|
||||||
* @returns JSON from the package.json file if it is valid, `null` otherwise.
|
* @returns JSON from the package.json file if it is valid, `null` otherwise.
|
||||||
*/
|
*/
|
||||||
function loadEntryPointPackage(
|
function loadEntryPointPackage(
|
||||||
fs: FileSystem, logger: Logger, packageJsonPath: AbsoluteFsPath): EntryPointPackageJson|null {
|
fs: FileSystem, logger: Logger, packageJsonPath: AbsoluteFsPath,
|
||||||
|
hasConfig: boolean): EntryPointPackageJson|null {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(fs.readFile(packageJsonPath));
|
return JSON.parse(fs.readFile(packageJsonPath));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// We may have run into a package.json with unexpected symbols
|
if (!hasConfig) {
|
||||||
logger.warn(`Failed to read entry point info from ${packageJsonPath} with error ${e}.`);
|
// We may have run into a package.json with unexpected symbols
|
||||||
|
logger.warn(`Failed to read entry point info from ${packageJsonPath} with error ${e}.`);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,3 +171,23 @@ function isUmdModule(fs: FileSystem, sourceFilePath: AbsoluteFsPath): boolean {
|
||||||
return sourceFile.statements.length > 0 &&
|
return sourceFile.statements.length > 0 &&
|
||||||
parseStatementForUmdModule(sourceFile.statements[0]) !== null;
|
parseStatementForUmdModule(sourceFile.statements[0]) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mergeConfigAndPackageJson(
|
||||||
|
entryPointPackageJson: EntryPointPackageJson | null,
|
||||||
|
entryPointConfig: NgccEntryPointConfig | undefined, packagePath: AbsoluteFsPath,
|
||||||
|
entryPointPath: AbsoluteFsPath): EntryPointPackageJson|null {
|
||||||
|
if (entryPointPackageJson !== null) {
|
||||||
|
if (entryPointConfig === undefined) {
|
||||||
|
return entryPointPackageJson;
|
||||||
|
} else {
|
||||||
|
return {...entryPointPackageJson, ...entryPointConfig.override};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (entryPointConfig === undefined) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
const name = `${basename(packagePath)}/${relative(packagePath, entryPointPath)}`;
|
||||||
|
return {name, ...entryPointConfig.override};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,12 +9,13 @@ import {AbsoluteFsPath, FileSystem, join, resolve} from '../../../src/ngtsc/file
|
||||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||||
import {Logger} from '../logging/logger';
|
import {Logger} from '../logging/logger';
|
||||||
import {PathMappings} from '../utils';
|
import {PathMappings} from '../utils';
|
||||||
|
import {NgccConfiguration} from './configuration';
|
||||||
import {EntryPoint, getEntryPointInfo} from './entry_point';
|
import {EntryPoint, getEntryPointInfo} from './entry_point';
|
||||||
|
|
||||||
export class EntryPointFinder {
|
export class EntryPointFinder {
|
||||||
constructor(
|
constructor(
|
||||||
private fs: FileSystem, private logger: Logger, private resolver: DependencyResolver) {}
|
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
||||||
|
private resolver: DependencyResolver) {}
|
||||||
/**
|
/**
|
||||||
* Search the given directory, and sub-directories, for Angular package entry points.
|
* Search the given directory, and sub-directories, for Angular package entry points.
|
||||||
* @param sourceDirectory An absolute path to the directory to search for entry points.
|
* @param sourceDirectory An absolute path to the directory to search for entry points.
|
||||||
|
@ -111,7 +112,8 @@ export class EntryPointFinder {
|
||||||
const entryPoints: EntryPoint[] = [];
|
const entryPoints: EntryPoint[] = [];
|
||||||
|
|
||||||
// Try to get an entry point from the top level package directory
|
// Try to get an entry point from the top level package directory
|
||||||
const topLevelEntryPoint = getEntryPointInfo(this.fs, this.logger, packagePath, packagePath);
|
const topLevelEntryPoint =
|
||||||
|
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, packagePath);
|
||||||
|
|
||||||
// If there is no primary entry-point then exit
|
// If there is no primary entry-point then exit
|
||||||
if (topLevelEntryPoint === null) {
|
if (topLevelEntryPoint === null) {
|
||||||
|
@ -120,8 +122,11 @@ export class EntryPointFinder {
|
||||||
|
|
||||||
// Otherwise store it and search for secondary entry-points
|
// Otherwise store it and search for secondary entry-points
|
||||||
entryPoints.push(topLevelEntryPoint);
|
entryPoints.push(topLevelEntryPoint);
|
||||||
this.walkDirectory(packagePath, subdir => {
|
this.walkDirectory(packagePath, packagePath, (path, isDirectory) => {
|
||||||
const subEntryPoint = getEntryPointInfo(this.fs, this.logger, packagePath, subdir);
|
// If the path is a JS file then strip its extension and see if we can match an entry-point.
|
||||||
|
const possibleEntryPointPath = isDirectory ? path : stripJsExtension(path);
|
||||||
|
const subEntryPoint =
|
||||||
|
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, possibleEntryPointPath);
|
||||||
if (subEntryPoint !== null) {
|
if (subEntryPoint !== null) {
|
||||||
entryPoints.push(subEntryPoint);
|
entryPoints.push(subEntryPoint);
|
||||||
}
|
}
|
||||||
|
@ -136,22 +141,29 @@ export class EntryPointFinder {
|
||||||
* @param dir the directory to recursively walk.
|
* @param dir the directory to recursively walk.
|
||||||
* @param fn the function to apply to each directory.
|
* @param fn the function to apply to each directory.
|
||||||
*/
|
*/
|
||||||
private walkDirectory(dir: AbsoluteFsPath, fn: (dir: AbsoluteFsPath) => void) {
|
private walkDirectory(
|
||||||
|
packagePath: AbsoluteFsPath, dir: AbsoluteFsPath,
|
||||||
|
fn: (path: AbsoluteFsPath, isDirectory: boolean) => void) {
|
||||||
return this.fs
|
return this.fs
|
||||||
.readdir(dir)
|
.readdir(dir)
|
||||||
// Not interested in hidden files
|
// Not interested in hidden files
|
||||||
.filter(p => !p.startsWith('.'))
|
.filter(path => !path.startsWith('.'))
|
||||||
// Ignore node_modules
|
// Ignore node_modules
|
||||||
.filter(p => p !== 'node_modules')
|
.filter(path => path !== 'node_modules')
|
||||||
// Only interested in directories (and only those that are not symlinks)
|
.map(path => resolve(dir, path))
|
||||||
.filter(p => {
|
.forEach(path => {
|
||||||
const stat = this.fs.lstat(resolve(dir, p));
|
const stat = this.fs.lstat(path);
|
||||||
return stat.isDirectory() && !stat.isSymbolicLink();
|
|
||||||
})
|
if (stat.isSymbolicLink()) {
|
||||||
.forEach(subDir => {
|
// We are not interested in symbolic links
|
||||||
const resolvedSubDir = resolve(dir, subDir);
|
return;
|
||||||
fn(resolvedSubDir);
|
}
|
||||||
this.walkDirectory(resolvedSubDir, fn);
|
|
||||||
|
fn(path, stat.isDirectory());
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
this.walkDirectory(packagePath, path, fn);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,3 +199,7 @@ function removeDeeperPaths(value: AbsoluteFsPath, index: number, array: Absolute
|
||||||
function values<T>(obj: {[key: string]: T}): T[] {
|
function values<T>(obj: {[key: string]: T}): T[] {
|
||||||
return Object.keys(obj).map(key => obj[key]);
|
return Object.keys(obj).map(key => obj[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stripJsExtension<T extends string>(filePath: T): T {
|
||||||
|
return filePath.replace(/\.js$/, '') as T;
|
||||||
|
}
|
||||||
|
|
|
@ -351,6 +351,92 @@ runInEachFileSystem(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with configuration files', () => {
|
||||||
|
it('should process a configured deep-import as an entry-point', () => {
|
||||||
|
loadTestFiles([
|
||||||
|
{
|
||||||
|
name: _('/ngcc.config.js'),
|
||||||
|
contents: `module.exports = { packages: {
|
||||||
|
'deep_import': {
|
||||||
|
entryPoints: {
|
||||||
|
'./entry_point': { override: { typings: '../entry_point.d.ts', es2015: '../entry_point.js' } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _('/node_modules/deep_import/package.json'),
|
||||||
|
contents: '{"name": "deep-import", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _('/node_modules/deep_import/entry_point.js'),
|
||||||
|
contents: `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
@Component({selector: 'entry-point'})
|
||||||
|
export class EntryPoint {}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _('/node_modules/deep_import/entry_point.d.ts'),
|
||||||
|
contents: `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
@Component({selector: 'entry-point'})
|
||||||
|
export class EntryPoint {}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
mainNgcc({
|
||||||
|
basePath: '/node_modules',
|
||||||
|
targetEntryPointPath: 'deep_import/entry_point',
|
||||||
|
propertiesToConsider: ['es2015']
|
||||||
|
});
|
||||||
|
// The containing package is not processed
|
||||||
|
expect(loadPackage('deep_import').__processed_by_ivy_ngcc__).toBeUndefined();
|
||||||
|
// But the configured entry-point and its dependency (@angular/core) are processed.
|
||||||
|
expect(loadPackage('deep_import/entry_point').__processed_by_ivy_ngcc__).toEqual({
|
||||||
|
es2015: '0.0.0-PLACEHOLDER',
|
||||||
|
typings: '0.0.0-PLACEHOLDER',
|
||||||
|
});
|
||||||
|
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||||
|
es2015: '0.0.0-PLACEHOLDER',
|
||||||
|
typings: '0.0.0-PLACEHOLDER',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not process ignored entry-points', () => {
|
||||||
|
loadTestFiles([
|
||||||
|
{
|
||||||
|
name: _('/ngcc.config.js'),
|
||||||
|
contents: `module.exports = { packages: {
|
||||||
|
'@angular/core': {
|
||||||
|
entryPoints: {
|
||||||
|
'./testing': {ignore: true}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'@angular/common': {
|
||||||
|
entryPoints: {
|
||||||
|
'.': {ignore: true}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}};`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
mainNgcc({basePath: '/node_modules', propertiesToConsider: ['es2015']});
|
||||||
|
// We process core but not core/testing.
|
||||||
|
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({
|
||||||
|
es2015: '0.0.0-PLACEHOLDER',
|
||||||
|
typings: '0.0.0-PLACEHOLDER',
|
||||||
|
});
|
||||||
|
expect(loadPackage('@angular/core/testing').__processed_by_ivy_ngcc__).toBeUndefined();
|
||||||
|
// We do not compile common but we do compile its sub-entry-points.
|
||||||
|
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__).toBeUndefined();
|
||||||
|
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__).toEqual({
|
||||||
|
es2015: '0.0.0-PLACEHOLDER',
|
||||||
|
typings: '0.0.0-PLACEHOLDER',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function loadPackage(
|
function loadPackage(
|
||||||
packageName: string, basePath: AbsoluteFsPath = _('/node_modules')): EntryPointPackageJson {
|
packageName: string, basePath: AbsoluteFsPath = _('/node_modules')): EntryPointPackageJson {
|
||||||
return JSON.parse(fs.readFile(fs.resolve(basePath, packageName, 'package.json')));
|
return JSON.parse(fs.readFile(fs.resolve(basePath, packageName, 'package.json')));
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. 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.io/license
|
||||||
|
*/
|
||||||
|
import {FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||||
|
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||||
|
import {loadTestFiles} from '../../../test/helpers';
|
||||||
|
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||||
|
|
||||||
|
|
||||||
|
runInEachFileSystem(() => {
|
||||||
|
let _Abs: typeof absoluteFrom;
|
||||||
|
let fs: FileSystem;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
_Abs = absoluteFrom;
|
||||||
|
fs = getFileSystem();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('NgccConfiguration', () => {
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should error if a project level config file is badly formatted', () => {
|
||||||
|
loadTestFiles([{name: _Abs('/project-1/ngcc.config.js'), contents: `bad js code`}]);
|
||||||
|
expect(() => new NgccConfiguration(fs, _Abs('/project-1')))
|
||||||
|
.toThrowError(
|
||||||
|
`Invalid project configuration file at "${_Abs('/project-1/ngcc.config.js')}": Unexpected identifier`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getConfig()', () => {
|
||||||
|
it('should return configuration for a package found in a package level file', () => {
|
||||||
|
loadTestFiles([{
|
||||||
|
name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'),
|
||||||
|
contents: `module.exports = {entryPoints: { './entry-point-1': {}}}`
|
||||||
|
}]);
|
||||||
|
const readFileSpy = spyOn(fs, 'readFile').and.callThrough();
|
||||||
|
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||||
|
const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1'));
|
||||||
|
|
||||||
|
expect(config).toEqual(
|
||||||
|
{entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}});
|
||||||
|
expect(readFileSpy)
|
||||||
|
.toHaveBeenCalledWith(_Abs('/project-1/node_modules/package-1/ngcc.config.js'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cache configuration for a package found in a package level file', () => {
|
||||||
|
loadTestFiles([{
|
||||||
|
name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'),
|
||||||
|
contents: `
|
||||||
|
module.exports = {
|
||||||
|
entryPoints: {
|
||||||
|
'./entry-point-1': {}
|
||||||
|
},
|
||||||
|
};`
|
||||||
|
}]);
|
||||||
|
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||||
|
|
||||||
|
// Populate the cache
|
||||||
|
configuration.getConfig(_Abs('/project-1/node_modules/package-1'));
|
||||||
|
|
||||||
|
const readFileSpy = spyOn(fs, 'readFile').and.callThrough();
|
||||||
|
const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1'));
|
||||||
|
|
||||||
|
expect(config).toEqual(
|
||||||
|
{entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}});
|
||||||
|
expect(readFileSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty configuration object if there is no matching config file', () => {
|
||||||
|
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||||
|
const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1'));
|
||||||
|
expect(config).toEqual({entryPoints: {}});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if a package level config file is badly formatted', () => {
|
||||||
|
loadTestFiles([{
|
||||||
|
name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'),
|
||||||
|
contents: `bad js code`
|
||||||
|
}]);
|
||||||
|
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||||
|
expect(() => configuration.getConfig(_Abs('/project-1/node_modules/package-1')))
|
||||||
|
.toThrowError(
|
||||||
|
`Invalid package configuration file at "${_Abs('/project-1/node_modules/package-1/ngcc.config.js')}": Unexpected identifier`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return configuration for a package found in a project level file', () => {
|
||||||
|
loadTestFiles([{
|
||||||
|
name: _Abs('/project-1/ngcc.config.js'),
|
||||||
|
contents: `
|
||||||
|
module.exports = {
|
||||||
|
packages: {
|
||||||
|
'package-1': {
|
||||||
|
entryPoints: {
|
||||||
|
'./entry-point-1': {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};`
|
||||||
|
}]);
|
||||||
|
const readFileSpy = spyOn(fs, 'readFile').and.callThrough();
|
||||||
|
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||||
|
expect(readFileSpy).toHaveBeenCalledWith(_Abs('/project-1/ngcc.config.js'));
|
||||||
|
|
||||||
|
const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1'));
|
||||||
|
expect(config).toEqual(
|
||||||
|
{entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override package level config with project level config per package', () => {
|
||||||
|
loadTestFiles([
|
||||||
|
{
|
||||||
|
name: _Abs('/project-1/ngcc.config.js'),
|
||||||
|
contents: `
|
||||||
|
module.exports = {
|
||||||
|
packages: {
|
||||||
|
'package-2': {
|
||||||
|
entryPoints: {
|
||||||
|
'./project-setting-entry-point': {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'),
|
||||||
|
contents: `
|
||||||
|
module.exports = {
|
||||||
|
entryPoints: {
|
||||||
|
'./package-setting-entry-point': {}
|
||||||
|
},
|
||||||
|
};`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _Abs('/project-1/node_modules/package-2/ngcc.config.js'),
|
||||||
|
contents: `
|
||||||
|
module.exports = {
|
||||||
|
entryPoints: {
|
||||||
|
'./package-setting-entry-point': {}
|
||||||
|
},
|
||||||
|
};`,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
const readFileSpy = spyOn(fs, 'readFile').and.callThrough();
|
||||||
|
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||||
|
expect(readFileSpy).toHaveBeenCalledWith(_Abs('/project-1/ngcc.config.js'));
|
||||||
|
|
||||||
|
const package1Config = configuration.getConfig(_Abs('/project-1/node_modules/package-1'));
|
||||||
|
expect(package1Config).toEqual({
|
||||||
|
entryPoints:
|
||||||
|
{[_Abs('/project-1/node_modules/package-1/package-setting-entry-point')]: {}}
|
||||||
|
});
|
||||||
|
expect(readFileSpy)
|
||||||
|
.toHaveBeenCalledWith(_Abs('/project-1/node_modules/package-1/ngcc.config.js'));
|
||||||
|
|
||||||
|
const package2Config = configuration.getConfig(_Abs('/project-1/node_modules/package-2'));
|
||||||
|
expect(package2Config).toEqual({
|
||||||
|
entryPoints:
|
||||||
|
{[_Abs('/project-1/node_modules/package-2/project-setting-entry-point')]: {}}
|
||||||
|
});
|
||||||
|
expect(readFileSpy)
|
||||||
|
.not.toHaveBeenCalledWith(_Abs('/project-1/node_modules/package-2/ngcc.config.js'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -11,6 +11,7 @@ import {loadTestFiles} from '../../../test/helpers';
|
||||||
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
||||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||||
|
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||||
import {EntryPoint} from '../../src/packages/entry_point';
|
import {EntryPoint} from '../../src/packages/entry_point';
|
||||||
import {EntryPointFinder} from '../../src/packages/entry_point_finder';
|
import {EntryPointFinder} from '../../src/packages/entry_point_finder';
|
||||||
import {MockLogger} from '../helpers/mock_logger';
|
import {MockLogger} from '../helpers/mock_logger';
|
||||||
|
@ -31,7 +32,8 @@ runInEachFileSystem(() => {
|
||||||
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
|
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
|
||||||
return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []};
|
return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []};
|
||||||
});
|
});
|
||||||
finder = new EntryPointFinder(fs, new MockLogger(), resolver);
|
finder =
|
||||||
|
new EntryPointFinder(fs, new NgccConfiguration(fs, _('/')), new MockLogger(), resolver);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find sub-entry-points within a package', () => {
|
it('should find sub-entry-points within a package', () => {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||||
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 {getEntryPointInfo} from '../../src/packages/entry_point';
|
import {getEntryPointInfo} from '../../src/packages/entry_point';
|
||||||
import {MockLogger} from '../helpers/mock_logger';
|
import {MockLogger} from '../helpers/mock_logger';
|
||||||
|
|
||||||
|
@ -19,8 +20,8 @@ runInEachFileSystem(() => {
|
||||||
let fs: FileSystem;
|
let fs: FileSystem;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
SOME_PACKAGE = absoluteFrom('/some_package');
|
|
||||||
_ = absoluteFrom;
|
_ = absoluteFrom;
|
||||||
|
SOME_PACKAGE = _('/project/node_modules/some_package');
|
||||||
fs = getFileSystem();
|
fs = getFileSystem();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28,51 +29,152 @@ runInEachFileSystem(() => {
|
||||||
() => {
|
() => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
{
|
{
|
||||||
name: _('/some_package/valid_entry_point/package.json'),
|
name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
|
||||||
contents: createPackageJson('valid_entry_point')
|
contents: createPackageJson('valid_entry_point')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: _('/some_package/valid_entry_point/valid_entry_point.metadata.json'),
|
name: _(
|
||||||
|
'/project/node_modules/some_package/valid_entry_point/valid_entry_point.metadata.json'),
|
||||||
contents: 'some meta data'
|
contents: 'some meta data'
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
const entryPoint = getEntryPointInfo(
|
const entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/valid_entry_point'));
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/valid_entry_point'));
|
||||||
expect(entryPoint).toEqual({
|
expect(entryPoint).toEqual({
|
||||||
name: 'some-package/valid_entry_point',
|
name: 'some_package/valid_entry_point',
|
||||||
package: SOME_PACKAGE,
|
package: SOME_PACKAGE,
|
||||||
path: _('/some_package/valid_entry_point'),
|
path: _('/project/node_modules/some_package/valid_entry_point'),
|
||||||
typings: _(`/some_package/valid_entry_point/valid_entry_point.d.ts`),
|
typings:
|
||||||
packageJson: loadPackageJson(fs, '/some_package/valid_entry_point'),
|
_(`/project/node_modules/some_package/valid_entry_point/valid_entry_point.d.ts`),
|
||||||
|
packageJson: loadPackageJson(fs, '/project/node_modules/some_package/valid_entry_point'),
|
||||||
compiledByAngular: true,
|
compiledByAngular: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return null if configured to ignore the specified entry-point', () => {
|
||||||
|
loadTestFiles([
|
||||||
|
{
|
||||||
|
name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
|
||||||
|
contents: createPackageJson('valid_entry_point'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _(
|
||||||
|
'/project/node_modules/some_package/valid_entry_point/valid_entry_point.metadata.json'),
|
||||||
|
contents: 'some meta data',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
|
spyOn(config, 'getConfig').and.returnValue({
|
||||||
|
entryPoints:
|
||||||
|
{[_('/project/node_modules/some_package/valid_entry_point')]: {ignore: true}}
|
||||||
|
});
|
||||||
|
const entryPoint = getEntryPointInfo(
|
||||||
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/valid_entry_point'));
|
||||||
|
expect(entryPoint).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override the properties on package.json if the entry-point is configured', () => {
|
||||||
|
loadTestFiles([
|
||||||
|
{
|
||||||
|
name: _('/project/node_modules/some_package/valid_entry_point/package.json'),
|
||||||
|
contents: createPackageJson('valid_entry_point'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: _(
|
||||||
|
'/project/node_modules/some_package/valid_entry_point/valid_entry_point.metadata.json'),
|
||||||
|
contents: 'some meta data',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
|
const override = {
|
||||||
|
typings: './some_other.d.ts',
|
||||||
|
esm2015: './some_other.js',
|
||||||
|
};
|
||||||
|
spyOn(config, 'getConfig').and.returnValue({
|
||||||
|
entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {override}}
|
||||||
|
});
|
||||||
|
const entryPoint = getEntryPointInfo(
|
||||||
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/valid_entry_point'));
|
||||||
|
const overriddenPackageJson = {
|
||||||
|
...loadPackageJson(fs, '/project/node_modules/some_package/valid_entry_point'),
|
||||||
|
...override};
|
||||||
|
expect(entryPoint).toEqual({
|
||||||
|
name: 'some_package/valid_entry_point',
|
||||||
|
package: SOME_PACKAGE,
|
||||||
|
path: _('/project/node_modules/some_package/valid_entry_point'),
|
||||||
|
typings: _('/project/node_modules/some_package/valid_entry_point/some_other.d.ts'),
|
||||||
|
packageJson: overriddenPackageJson,
|
||||||
|
compiledByAngular: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return null if there is no package.json at the entry-point path', () => {
|
it('should return null if there is no package.json at the entry-point path', () => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
{
|
{
|
||||||
name: _('/some_package/missing_package_json/missing_package_json.metadata.json'),
|
name: _(
|
||||||
|
'/project/node_modules/some_package/missing_package_json/missing_package_json.metadata.json'),
|
||||||
contents: 'some meta data'
|
contents: 'some meta data'
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
const entryPoint = getEntryPointInfo(
|
const entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_package_json'));
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/missing_package_json'));
|
||||||
expect(entryPoint).toBe(null);
|
expect(entryPoint).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return a configured entry-point if there is no package.json at the entry-point path',
|
||||||
|
() => {
|
||||||
|
loadTestFiles([
|
||||||
|
// no package.json!
|
||||||
|
{
|
||||||
|
name: _(
|
||||||
|
'/project/node_modules/some_package/missing_package_json/missing_package_json.metadata.json'),
|
||||||
|
contents: 'some meta data',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
|
const override =
|
||||||
|
JSON.parse(createPackageJson('missing_package_json', {excludes: ['name']}));
|
||||||
|
spyOn(config, 'getConfig').and.returnValue({
|
||||||
|
entryPoints:
|
||||||
|
{[_('/project/node_modules/some_package/missing_package_json')]: {override}}
|
||||||
|
});
|
||||||
|
const entryPoint = getEntryPointInfo(
|
||||||
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/missing_package_json'));
|
||||||
|
expect(entryPoint).toEqual({
|
||||||
|
name: 'some_package/missing_package_json',
|
||||||
|
package: SOME_PACKAGE,
|
||||||
|
path: _('/project/node_modules/some_package/missing_package_json'),
|
||||||
|
typings: _(
|
||||||
|
'/project/node_modules/some_package/missing_package_json/missing_package_json.d.ts'),
|
||||||
|
packageJson: {name: 'some_package/missing_package_json', ...override},
|
||||||
|
compiledByAngular: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return null if there is no typings or types field in the package.json', () => {
|
it('should return null if there is no typings or types field in the package.json', () => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
{
|
{
|
||||||
name: _('/some_package/missing_typings/package.json'),
|
name: _('/project/node_modules/some_package/missing_typings/package.json'),
|
||||||
contents: createPackageJson('missing_typings', {excludes: ['typings']})
|
contents: createPackageJson('missing_typings', {excludes: ['typings']})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: _('/some_package/missing_typings/missing_typings.metadata.json'),
|
name:
|
||||||
|
_('/project/node_modules/some_package/missing_typings/missing_typings.metadata.json'),
|
||||||
contents: 'some meta data'
|
contents: 'some meta data'
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const entryPoint =
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_typings'));
|
const entryPoint = getEntryPointInfo(
|
||||||
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/missing_typings'));
|
||||||
expect(entryPoint).toBe(null);
|
expect(entryPoint).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,42 +182,74 @@ runInEachFileSystem(() => {
|
||||||
() => {
|
() => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
{
|
{
|
||||||
name: _('/some_package/missing_metadata/package.json'),
|
name: _('/project/node_modules/some_package/missing_metadata/package.json'),
|
||||||
contents: createPackageJson('missing_metadata')
|
contents: createPackageJson('missing_metadata')
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
const entryPoint = getEntryPointInfo(
|
const entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/missing_metadata'));
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/missing_metadata'));
|
||||||
expect(entryPoint).toEqual({
|
expect(entryPoint).toEqual({
|
||||||
name: 'some-package/missing_metadata',
|
name: 'some_package/missing_metadata',
|
||||||
package: SOME_PACKAGE,
|
package: SOME_PACKAGE,
|
||||||
path: _('/some_package/missing_metadata'),
|
path: _('/project/node_modules/some_package/missing_metadata'),
|
||||||
typings: _(`/some_package/missing_metadata/missing_metadata.d.ts`),
|
typings: _(`/project/node_modules/some_package/missing_metadata/missing_metadata.d.ts`),
|
||||||
packageJson: loadPackageJson(fs, '/some_package/missing_metadata'),
|
packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_metadata'),
|
||||||
compiledByAngular: false,
|
compiledByAngular: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return an object with `compiledByAngular` set to true if there is no metadata.json file but the entry-point has a configuration',
|
||||||
|
() => {
|
||||||
|
loadTestFiles([
|
||||||
|
{
|
||||||
|
name: _('/project/node_modules/some_package/missing_metadata/package.json'),
|
||||||
|
contents: createPackageJson('missing_metadata'),
|
||||||
|
},
|
||||||
|
// no metadata.json!
|
||||||
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
|
spyOn(config, 'getConfig').and.returnValue({
|
||||||
|
entryPoints: {[_('/project/node_modules/some_package/missing_metadata')]: {}}
|
||||||
|
});
|
||||||
|
const entryPoint = getEntryPointInfo(
|
||||||
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/missing_metadata'));
|
||||||
|
expect(entryPoint).toEqual({
|
||||||
|
name: 'some_package/missing_metadata',
|
||||||
|
package: SOME_PACKAGE,
|
||||||
|
path: _('/project/node_modules/some_package/missing_metadata'),
|
||||||
|
typings: _('/project/node_modules/some_package/missing_metadata/missing_metadata.d.ts'),
|
||||||
|
packageJson: loadPackageJson(fs, '/project/node_modules/some_package/missing_metadata'),
|
||||||
|
compiledByAngular: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should work if the typings field is named `types', () => {
|
it('should work if the typings field is named `types', () => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
{
|
{
|
||||||
name: _('/some_package/types_rather_than_typings/package.json'),
|
name: _('/project/node_modules/some_package/types_rather_than_typings/package.json'),
|
||||||
contents: createPackageJson('types_rather_than_typings', {}, 'types')
|
contents: createPackageJson('types_rather_than_typings', {}, 'types')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name:
|
name: _(
|
||||||
_('/some_package/types_rather_than_typings/types_rather_than_typings.metadata.json'),
|
'/project/node_modules/some_package/types_rather_than_typings/types_rather_than_typings.metadata.json'),
|
||||||
contents: 'some meta data'
|
contents: 'some meta data'
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
const entryPoint = getEntryPointInfo(
|
const entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/types_rather_than_typings'));
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/types_rather_than_typings'));
|
||||||
expect(entryPoint).toEqual({
|
expect(entryPoint).toEqual({
|
||||||
name: 'some-package/types_rather_than_typings',
|
name: 'some_package/types_rather_than_typings',
|
||||||
package: SOME_PACKAGE,
|
package: SOME_PACKAGE,
|
||||||
path: _('/some_package/types_rather_than_typings'),
|
path: _('/project/node_modules/some_package/types_rather_than_typings'),
|
||||||
typings: _(`/some_package/types_rather_than_typings/types_rather_than_typings.d.ts`),
|
typings: _(
|
||||||
packageJson: loadPackageJson(fs, '/some_package/types_rather_than_typings'),
|
`/project/node_modules/some_package/types_rather_than_typings/types_rather_than_typings.d.ts`),
|
||||||
|
packageJson:
|
||||||
|
loadPackageJson(fs, '/project/node_modules/some_package/types_rather_than_typings'),
|
||||||
compiledByAngular: true,
|
compiledByAngular: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -123,7 +257,7 @@ runInEachFileSystem(() => {
|
||||||
it('should work with Angular Material style package.json', () => {
|
it('should work with Angular Material style package.json', () => {
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
{
|
{
|
||||||
name: _('/some_package/material_style/package.json'),
|
name: _('/project/node_modules/some_package/material_style/package.json'),
|
||||||
contents: `{
|
contents: `{
|
||||||
"name": "some_package/material_style",
|
"name": "some_package/material_style",
|
||||||
"typings": "./material_style.d.ts",
|
"typings": "./material_style.d.ts",
|
||||||
|
@ -133,18 +267,20 @@ runInEachFileSystem(() => {
|
||||||
}`
|
}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: _('/some_package/material_style/material_style.metadata.json'),
|
name: _('/project/node_modules/some_package/material_style/material_style.metadata.json'),
|
||||||
contents: 'some meta data'
|
contents: 'some meta data'
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const entryPoint =
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
getEntryPointInfo(fs, new MockLogger(), SOME_PACKAGE, _('/some_package/material_style'));
|
const entryPoint = getEntryPointInfo(
|
||||||
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/material_style'));
|
||||||
expect(entryPoint).toEqual({
|
expect(entryPoint).toEqual({
|
||||||
name: 'some_package/material_style',
|
name: 'some_package/material_style',
|
||||||
package: SOME_PACKAGE,
|
package: SOME_PACKAGE,
|
||||||
path: _('/some_package/material_style'),
|
path: _('/project/node_modules/some_package/material_style'),
|
||||||
typings: _(`/some_package/material_style/material_style.d.ts`),
|
typings: _(`/project/node_modules/some_package/material_style/material_style.d.ts`),
|
||||||
packageJson: loadPackageJson(fs, '/some_package/material_style'),
|
packageJson: loadPackageJson(fs, '/project/node_modules/some_package/material_style'),
|
||||||
compiledByAngular: true,
|
compiledByAngular: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -155,12 +291,14 @@ runInEachFileSystem(() => {
|
||||||
// for example, @schematics/angular contains a package.json blueprint
|
// for example, @schematics/angular contains a package.json blueprint
|
||||||
// with unexpected symbols
|
// with unexpected symbols
|
||||||
{
|
{
|
||||||
name: _('/some_package/unexpected_symbols/package.json'),
|
name: _('/project/node_modules/some_package/unexpected_symbols/package.json'),
|
||||||
contents: '{"devDependencies": {<% if (!minimal) { %>"@types/jasmine": "~2.8.8" <% } %>}}'
|
contents: '{"devDependencies": {<% if (!minimal) { %>"@types/jasmine": "~2.8.8" <% } %>}}'
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const config = new NgccConfiguration(fs, _('/project'));
|
||||||
const entryPoint = getEntryPointInfo(
|
const entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), SOME_PACKAGE, _('/some_package/unexpected_symbols'));
|
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||||
|
_('/project/node_modules/some_package/unexpected_symbols'));
|
||||||
expect(entryPoint).toBe(null);
|
expect(entryPoint).toBe(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -169,7 +307,7 @@ runInEachFileSystem(() => {
|
||||||
packageName: string, {excludes}: {excludes?: string[]} = {},
|
packageName: string, {excludes}: {excludes?: string[]} = {},
|
||||||
typingsProp: string = 'typings'): string {
|
typingsProp: string = 'typings'): string {
|
||||||
const packageJson: any = {
|
const packageJson: any = {
|
||||||
name: `some-package/${packageName}`,
|
name: `some_package/${packageName}`,
|
||||||
[typingsProp]: `./${packageName}.d.ts`,
|
[typingsProp]: `./${packageName}.d.ts`,
|
||||||
fesm2015: `./fesm2015/${packageName}.js`,
|
fesm2015: `./fesm2015/${packageName}.js`,
|
||||||
esm2015: `./esm2015/${packageName}.js`,
|
esm2015: `./esm2015/${packageName}.js`,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import {FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
import {FileSystem, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||||
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 {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointInfo} from '../../src/packages/entry_point';
|
import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointInfo} from '../../src/packages/entry_point';
|
||||||
import {EntryPointBundle, makeEntryPointBundle} from '../../src/packages/entry_point_bundle';
|
import {EntryPointBundle, makeEntryPointBundle} from '../../src/packages/entry_point_bundle';
|
||||||
import {FileWriter} from '../../src/writing/file_writer';
|
import {FileWriter} from '../../src/writing/file_writer';
|
||||||
|
@ -86,8 +87,9 @@ runInEachFileSystem(() => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fs = getFileSystem();
|
fs = getFileSystem();
|
||||||
fileWriter = new NewEntryPointFileWriter(fs);
|
fileWriter = new NewEntryPointFileWriter(fs);
|
||||||
|
const config = new NgccConfiguration(fs, _('/'));
|
||||||
entryPoint = getEntryPointInfo(
|
entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test')) !;
|
fs, config, new MockLogger(), _('/node_modules/test'), _('/node_modules/test')) !;
|
||||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||||
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
||||||
});
|
});
|
||||||
|
@ -174,8 +176,9 @@ runInEachFileSystem(() => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fs = getFileSystem();
|
fs = getFileSystem();
|
||||||
fileWriter = new NewEntryPointFileWriter(fs);
|
fileWriter = new NewEntryPointFileWriter(fs);
|
||||||
|
const config = new NgccConfiguration(fs, _('/'));
|
||||||
entryPoint = getEntryPointInfo(
|
entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/a')) !;
|
fs, config, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/a')) !;
|
||||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||||
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
||||||
});
|
});
|
||||||
|
@ -251,8 +254,9 @@ runInEachFileSystem(() => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fs = getFileSystem();
|
fs = getFileSystem();
|
||||||
fileWriter = new NewEntryPointFileWriter(fs);
|
fileWriter = new NewEntryPointFileWriter(fs);
|
||||||
|
const config = new NgccConfiguration(fs, _('/'));
|
||||||
entryPoint = getEntryPointInfo(
|
entryPoint = getEntryPointInfo(
|
||||||
fs, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b')) !;
|
fs, config, new MockLogger(), _('/node_modules/test'), _('/node_modules/test/b')) !;
|
||||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||||
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
esm2015bundle = makeTestBundle(fs, entryPoint, 'es2015', 'esm2015');
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue