Skip to content

Commit ee5949b

Browse files
DavidParks8dherges
authored andcommitted
fix: prevent accidental secondary entry resolution (#229)
BREAKING CHANGES: for auto-discovery of secondary entries, `package.json` files are now validated whether a `ngPackage` property exists; the value can be an empty object. This is a breaking change for a feature introduced in release candidate versions 1.5.0-rc.0/1.5.0-rc.1. **When upgrading from 1.4.x to 1.5.0, it is not-breaking**.
1 parent 0e6a7fa commit ee5949b

File tree

8 files changed

+73
-14
lines changed

8 files changed

+73
-14
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,12 @@ my_package
128128

129129
The contents of the secondary `package.json` can be as simple as:
130130
```json
131+
{
132+
ngPackage: {}
133+
}
131134
```
132135

133-
No, that is not a typo. No name is required. No version is required. Not even a json object is required.
136+
No, that is not a typo. No name is required. No version is required.
134137
It's all handled for you by ng-packagr!
135138
When built, the secondary bundles would be accessible as `$(your-primary-package-name)/testing`.
136139

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1 class="foo">bar!</h1>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$color: '#ff0000';
2+
3+
.foo {
4+
background-color: $color;
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'bar-component',
5+
templateUrl: './bar.component.html',
6+
styleUrls: ['./bar.component.scss']
7+
})
8+
export class BarComponent {
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './bar/bar.component';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"peerDependencies": {
3+
"@angular/core": "^4.1.2",
4+
"@angular/common": "^4.1.2"
5+
}
6+
}

integration/samples/secondary/specs/package.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ describe(`@sample/secondary`, () => {
1515
expect(PACKAGE).to.be.ok;
1616
});
1717

18+
it(`should not have ngPackage field`, () => {
19+
expect(PACKAGE.ngPackage).to.be.undefined;
20+
});
21+
1822
it(`should be named '@sample/secondary-lib'`, () => {
1923
expect(PACKAGE['name']).to.equal('@sample/secondary-lib');
2024
});
@@ -47,6 +51,10 @@ describe(`@sample/secondary`, () => {
4751
expect(PACKAGE).to.be.ok;
4852
});
4953

54+
it(`should not have ngPackage field`, () => {
55+
expect(PACKAGE.ngPackage).to.be.undefined;
56+
});
57+
5058
it(`should be named '@sample/secondary-lib/sub-module'`, () => {
5159
expect(PACKAGE['name']).to.equal('@sample/secondary-lib/sub-module');
5260
});
@@ -68,4 +76,12 @@ describe(`@sample/secondary`, () => {
6876
});
6977
});
7078

79+
describe(`should-be-ignored/package.json`, () => {
80+
const BASE = path.resolve(__dirname, '..', 'dist', 'should-be-ignored');
81+
82+
it(`should not exist`, () => {
83+
expect(() => fs.readFileSync(`${BASE}/package.json`, 'utf-8')).throw();
84+
});
85+
});
86+
7187
});

src/lib/steps/package.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import * as path from 'path';
22
import { NgPackageConfig } from '../../ng-package.schema';
33
import { NgPackageData, DEFAULT_BUILD_FOLDER } from '../model/ng-package-data';
44
import { NgArtifacts } from '../model/ng-artifacts';
5-
import { copyFiles } from '../util/copy';
65
import { tryReadJson } from '../util/json';
76
import { readJson, writeJson, readdir, lstat, Stats } from 'fs-extra';
87
import { merge, isArray } from 'lodash';
98
import * as log from '../util/log';
109
import { PackageSearchResult } from '../model/package-search-result';
1110

11+
const PACKAGE_JSON_FILE_NAME = 'package.json';
12+
1213
// this prevents array objects from getting merged to each other one by one
1314
function arrayMergeLogic(objValue, srcValue) {
1415
if (isArray(objValue)) {
@@ -29,7 +30,7 @@ function resolvePaths(workingDirectory: string, packageConfig: NgPackageConfig):
2930
}
3031
};
3132

32-
async function readNgPackageFile(filePath: string): Promise<NgPackageConfig> {
33+
async function readNgPackageFile(filePath: string): Promise<NgPackageConfig | null> {
3334

3435
log.debug('Searching for ng-package config at ' + filePath);
3536
try {
@@ -42,7 +43,7 @@ async function readNgPackageFile(filePath: string): Promise<NgPackageConfig> {
4243
if (error.code === 'ENOENT') {
4344
log.debug('ng-package config file not found');
4445
// if the file does not exist, that's ok
45-
return {};
46+
return null;
4647
}
4748
throw error;
4849
}
@@ -102,7 +103,7 @@ async function findSecondaryPackagePaths(rootPackage: NgPackageData): Promise<st
102103
if (pathStats.isDirectory() && !shouldExcludeFromDirectorySearch(fileSystemPath, foldersToExclude)) {
103104
directoriesToSearch.push(fullPath);
104105
} else if (!packageFileFound && pathStats.isFile()) {
105-
if (fileSystemPath.endsWith('package.json')) {
106+
if (fileSystemPath.endsWith(PACKAGE_JSON_FILE_NAME)) {
106107
packagePaths.push(fullPath);
107108
packageFileFound = true;
108109
// we can't `break` here because doing so might cause us to miss some directories
@@ -133,14 +134,16 @@ async function readRootPackage(filePath: string): Promise<NgPackageData> {
133134
const baseDirectory = path.dirname(filePath);
134135

135136
// read custom ng-package config file
136-
let promiseChain: Promise<NgPackageConfig> = readNgPackageFile(filePath);
137+
let promiseChain: Promise<NgPackageConfig> = readNgPackageFile(filePath)
138+
.then((ngPkg: NgPackageConfig | null) => ngPkg || {});
137139

138140
const defaultPath = path.join(baseDirectory, 'ng-package.json');
139141
if (defaultPath !== filePath) {
140142
// read default ng-package config file
141143
promiseChain = promiseChain.then(async (ngPkg: NgPackageConfig) => {
142-
const otherNgPkg: NgPackageConfig = await readNgPackageFile(defaultPath);
144+
const otherNgPkg: NgPackageConfig | null = await readNgPackageFile(defaultPath);
143145
// merge both ng-package config objects
146+
// merge will never return null
144147
return merge(ngPkg, otherNgPkg, arrayMergeLogic);
145148
});
146149
}
@@ -152,7 +155,7 @@ async function readRootPackage(filePath: string): Promise<NgPackageData> {
152155
// read 'package.json'
153156
log.debug('loading package.json');
154157

155-
const pkg: any = await readJson(path.resolve(packageConfigurationDirectory, 'package.json'));
158+
const pkg: any = await readJson(path.resolve(packageConfigurationDirectory, PACKAGE_JSON_FILE_NAME));
156159
// merge package.json ng-package config
157160
const finalPackageConfig = merge(ngPkg, pkg.ngPackage, arrayMergeLogic);
158161
// make sure we provide default values for src and dest
@@ -168,14 +171,22 @@ async function readRootPackage(filePath: string): Promise<NgPackageData> {
168171
);
169172
}
170173

171-
async function readSecondaryPackage(rootPackage: NgPackageData, filePath: string): Promise<NgPackageData> {
174+
async function readSecondaryPackage(rootPackage: NgPackageData, filePath: string): Promise<NgPackageData | null> {
172175
const baseDirectory = path.dirname(filePath);
173176
const ngPackageFile = path.resolve(baseDirectory, 'ng-package.json');
174-
const packageJsonFile = path.resolve(baseDirectory, 'package.json');
177+
const packageJsonFile = path.resolve(baseDirectory, PACKAGE_JSON_FILE_NAME);
175178

176-
let ngPackage: NgPackageConfig = await readNgPackageFile(ngPackageFile);
179+
let ngPackage: NgPackageConfig | null = await readNgPackageFile(ngPackageFile);
177180
const packageJson: any = await tryReadJson(packageJsonFile);
178181

182+
// if we don't detect any explicit package configurations, then ignore
183+
if (!ngPackage) {
184+
if (!packageJson || !packageJson.ngPackage) {
185+
log.debug(`No secondary package found in ${baseDirectory}`);
186+
return null;
187+
}
188+
}
189+
179190
ngPackage = merge(ngPackage, packageJson.ngPackage, arrayMergeLogic);
180191
if (!ngPackage.lib) {
181192
ngPackage.lib = {};
@@ -212,7 +223,10 @@ export async function discoverPackages(rootPath: string): Promise<PackageSearchR
212223
const secondaryPackagePromises: Promise<NgPackageData>[] = secondaryPackagePaths
213224
.map(secondaryPackagePath => readSecondaryPackage(rootPackage, secondaryPackagePath));
214225

215-
const secondaryPackages: NgPackageData[] = await Promise.all(secondaryPackagePromises);
226+
const secondaryPackagesFromPaths: (NgPackageData | null)[] = await Promise.all(secondaryPackagePromises);
227+
228+
// The packages that are null should be excluded because they were not explicitly meant to be secondary entries
229+
const secondaryPackages: NgPackageData[] = secondaryPackagesFromPaths.filter(x => !!x);
216230

217231
return {
218232
rootPackage,
@@ -231,13 +245,17 @@ export async function discoverPackages(rootPath: string): Promise<PackageSearchR
231245
export async function writePackage(ngPkg: NgPackageData, packageArtifacts: NgArtifacts): Promise<void> {
232246

233247
log.debug('writePackage');
234-
const packageJson: any = await readJson(path.resolve(ngPkg.sourcePath, 'package.json'));
248+
const packageJson: any = await readJson(path.resolve(ngPkg.sourcePath, PACKAGE_JSON_FILE_NAME));
235249
// set additional properties
236250
for(const fieldName in packageArtifacts) {
237251
packageJson[fieldName] = packageArtifacts[fieldName];
238252
}
239253

240254
packageJson.name = ngPkg.fullPackageName;
241255

242-
await writeJson(`${ngPkg.destinationPath}/package.json`, packageJson);
256+
// keep the dist package.json clean
257+
// this will not throw if ngPackage field does not exist
258+
delete packageJson.ngPackage;
259+
260+
await writeJson(path.resolve(ngPkg.destinationPath, PACKAGE_JSON_FILE_NAME), packageJson);
243261
}

0 commit comments

Comments
 (0)