feat(ivy): show error when trying to publish NGCC'd packages (#32031)
Publishing of NGCC packages should not be allowed. It is easy for a user to publish an NGCC'd version of a library they have workspace libraries which are being used in a workspace application. If a users builds a library and afterwards the application, the library will be transformed with NGCC and since NGCC taints the distributed files that should be published. With this change we use the npm/yarn `prepublishOnly` hook to display and error and abort the process with a non zero error code when a user tries to publish an NGCC version of the package. More info: https://docs.npmjs.com/misc/scripts PR Close #32031
This commit is contained in:
parent
f7eebd0227
commit
46304a4f83
|
@ -57,6 +57,17 @@ export function markAsProcessed(
|
||||||
processed[prop] = NGCC_VERSION;
|
processed[prop] = NGCC_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scripts = packageJson.scripts || (packageJson.scripts = {});
|
||||||
|
scripts.prepublishOnly__ivy_ngcc_bak =
|
||||||
|
scripts.prepublishOnly__ivy_ngcc_bak || scripts.prepublishOnly;
|
||||||
|
|
||||||
|
scripts.prepublishOnly = 'node --eval \"console.error(\'' +
|
||||||
|
'ERROR: Trying to publish a package that has been compiled by NGCC. This is not allowed.\\n' +
|
||||||
|
'Please delete and rebuild the package, without compiling with NGCC, before attempting to publish.\\n' +
|
||||||
|
'Note that NGCC may have been run by importing this package into another project that is being built with Ivy enabled.\\n' +
|
||||||
|
'\')\" ' +
|
||||||
|
'&& exit 1';
|
||||||
|
|
||||||
// Just in case this package.json was synthesized due to a custom configuration
|
// 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.
|
// we will ensure that the path to the containing folder exists before we write the file.
|
||||||
fs.ensureDir(dirname(packageJsonPath));
|
fs.ensureDir(dirname(packageJsonPath));
|
||||||
|
|
|
@ -55,7 +55,8 @@ export interface PackageJsonFormatProperties {
|
||||||
*/
|
*/
|
||||||
export interface EntryPointPackageJson extends PackageJsonFormatProperties {
|
export interface EntryPointPackageJson extends PackageJsonFormatProperties {
|
||||||
name: string;
|
name: string;
|
||||||
__processed_by_ivy_ngcc__?: {[key: string]: string};
|
scripts?: Record<string, string>;
|
||||||
|
__processed_by_ivy_ngcc__?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EntryPointJsonProperty = Exclude<keyof PackageJsonFormatProperties, 'types'|'typings'>;
|
export type EntryPointJsonProperty = Exclude<keyof PackageJsonFormatProperties, 'types'|'typings'>;
|
||||||
|
|
|
@ -84,6 +84,7 @@ runInEachFileSystem(() => {
|
||||||
const fs = getFileSystem();
|
const fs = getFileSystem();
|
||||||
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||||
|
expect(pkg.scripts).toBeUndefined();
|
||||||
|
|
||||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5']);
|
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5']);
|
||||||
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||||
|
@ -91,6 +92,7 @@ runInEachFileSystem(() => {
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBeUndefined();
|
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBeUndefined();
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
||||||
|
expect(pkg.scripts.prepublishOnly).toBeDefined();
|
||||||
|
|
||||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['esm2015', 'esm5']);
|
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['esm2015', 'esm5']);
|
||||||
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||||
|
@ -98,6 +100,7 @@ runInEachFileSystem(() => {
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBe('0.0.0-PLACEHOLDER');
|
||||||
|
expect(pkg.scripts.prepublishOnly).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the packageJson object in-place', () => {
|
it('should update the packageJson object in-place', () => {
|
||||||
|
@ -105,18 +108,21 @@ runInEachFileSystem(() => {
|
||||||
const fs = getFileSystem();
|
const fs = getFileSystem();
|
||||||
const pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
const pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||||
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
expect(pkg.__processed_by_ivy_ngcc__).toBeUndefined();
|
||||||
|
expect(pkg.scripts).toBeUndefined();
|
||||||
|
|
||||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5']);
|
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5']);
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBeUndefined();
|
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBeUndefined();
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBeUndefined();
|
||||||
|
expect(pkg.scripts.prepublishOnly).toBeDefined();
|
||||||
|
|
||||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['esm2015', 'esm5']);
|
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['esm2015', 'esm5']);
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.fesm2015).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.fesm5).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.esm2015).toBe('0.0.0-PLACEHOLDER');
|
||||||
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBe('0.0.0-PLACEHOLDER');
|
expect(pkg.__processed_by_ivy_ngcc__.esm5).toBe('0.0.0-PLACEHOLDER');
|
||||||
|
expect(pkg.scripts.prepublishOnly).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should one perform one write operation for all updated properties', () => {
|
it('should one perform one write operation for all updated properties', () => {
|
||||||
|
@ -128,6 +134,19 @@ runInEachFileSystem(() => {
|
||||||
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5', 'esm2015', 'esm5']);
|
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015', 'fesm5', 'esm2015', 'esm5']);
|
||||||
expect(writeFileSpy).toHaveBeenCalledTimes(1);
|
expect(writeFileSpy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should keep backup of existing 'prepublishOnly' script`, () => {
|
||||||
|
const COMMON_PACKAGE_PATH = _('/node_modules/@angular/common/package.json');
|
||||||
|
const fs = getFileSystem();
|
||||||
|
const prepublishOnly = 'existing script';
|
||||||
|
let pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||||
|
pkg.scripts = {prepublishOnly};
|
||||||
|
|
||||||
|
markAsProcessed(fs, pkg, COMMON_PACKAGE_PATH, ['fesm2015']);
|
||||||
|
pkg = JSON.parse(fs.readFile(COMMON_PACKAGE_PATH));
|
||||||
|
expect(pkg.scripts.prepublishOnly).toContain('This is not allowed');
|
||||||
|
expect(pkg.scripts.prepublishOnly__ivy_ngcc_bak).toBe(prepublishOnly);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasBeenProcessed', () => {
|
describe('hasBeenProcessed', () => {
|
||||||
|
|
Loading…
Reference in New Issue