-
Notifications
You must be signed in to change notification settings - Fork 295
/
write-package.transform.ts
145 lines (129 loc) · 6.11 KB
/
write-package.transform.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import * as fs from 'fs-extra';
import * as path from 'path';
import { Transform, transformFromPromise } from '../../brocc/transform';
import { NgEntryPoint } from '../../ng-package-format/entry-point';
import { NgPackage } from '../../ng-package-format/package';
import { ensureUnixPath } from '../../util/path';
import { rimraf } from '../../util/rimraf';
import * as log from '../../util/log';
import { globFiles } from '../../util/glob';
import { EntryPointNode, isEntryPointInProgress } from '../nodes';
import { copyFile } from '../../util/copy';
export const writePackageTransform: Transform = transformFromPromise(async graph => {
const entryPoint = graph.find(isEntryPointInProgress()) as EntryPointNode;
const ngEntryPoint: NgEntryPoint = entryPoint.data.entryPoint;
const ngPackage: NgPackage = graph.find(node => node.type === 'application/ng-package').data;
const { destinationFiles } = entryPoint.data;
// 5. COPY SOURCE FILES TO DESTINATION
log.info('Copying declaration files');
// we don't want to copy `dist` and 'node_modules' declaration files but only files in source
const declarationFiles = await globFiles(`${path.dirname(ngEntryPoint.entryFilePath)}/**/*.d.ts`, {
ignore: ['**/node_modules/**', `${ngPackage.dest}/**`]
});
if (declarationFiles.length) {
await Promise.all(
declarationFiles.map(value => {
const relativePath = path.relative(ngEntryPoint.entryFilePath, value);
const destination = path.resolve(destinationFiles.declarations, relativePath);
return copyFile(value, destination);
})
);
}
// 6. WRITE PACKAGE.JSON
log.info('Writing package metadata');
const relativeUnixFromDestPath = (filePath: string) =>
ensureUnixPath(path.relative(ngEntryPoint.destinationPath, filePath));
await writePackageJson(ngEntryPoint, ngPackage, {
main: relativeUnixFromDestPath(destinationFiles.umd),
module: relativeUnixFromDestPath(destinationFiles.fesm5),
es2015: relativeUnixFromDestPath(destinationFiles.fesm2015),
esm5: relativeUnixFromDestPath(destinationFiles.esm5),
esm2015: relativeUnixFromDestPath(destinationFiles.esm2015),
fesm5: relativeUnixFromDestPath(destinationFiles.fesm5),
fesm2015: relativeUnixFromDestPath(destinationFiles.fesm2015),
typings: relativeUnixFromDestPath(destinationFiles.declarations),
// XX 'metadata' property in 'package.json' is non-standard. Keep it anyway?
metadata: relativeUnixFromDestPath(destinationFiles.metadata),
// webpack v4+ specific flag to enable advanced optimizations and code splitting
sideEffects: ngEntryPoint.sideEffects
});
log.success(`Built ${ngEntryPoint.moduleId}`);
return graph;
});
/**
* Creates and writes a `package.json` file of the entry point used by the `node_module`
* resolution strategies.
*
* #### Example
*
* A consumer of the entry point depends on it by `import {..} from '@my/module/id';`.
* The module id `@my/module/id` will be resolved to the `package.json` file that is written by
* this build step.
* The properties `main`, `module`, `typings` (and so on) in the `package.json` point to the
* flattened JavaScript bundles, type definitions, (...).
*
* @param entryPoint An entry point of an Angular package / library
* @param additionalProperties Additional properties, e.g. binary artefacts (bundle files), to merge into `package.json`
*/
export async function writePackageJson(
entryPoint: NgEntryPoint,
pkg: NgPackage,
additionalProperties: { [key: string]: string | boolean | string[] }
): Promise<void> {
log.debug('Writing package.json');
// set additional properties
const packageJson = { ...entryPoint.packageJson, ...additionalProperties };
// read tslib version from `@angular/compiler` so that our tslib
// version at least matches that of angular if we use require('tslib').version
// it will get what installed and not the minimum version nor if it is a `~` or `^`
// this is only required for primary
if (!entryPoint.isSecondaryEntryPoint && !(packageJson.dependencies && packageJson.dependencies.tslib)) {
const { dependencies: angularDependencies = {} } = require('@angular/compiler/package.json');
const tsLibVersion = angularDependencies.tslib;
if (tsLibVersion) {
packageJson.dependencies = {
...packageJson.dependencies,
tslib: tsLibVersion
};
}
}
// Verify non-peerDependencies as they can easily lead to duplicate installs or version conflicts
// in the node_modules folder of an application
const whitelist = pkg.whitelistedNonPeerDependencies.map(value => new RegExp(value));
try {
checkNonPeerDependencies(packageJson, 'dependencies', whitelist);
} catch (e) {
await rimraf(entryPoint.destinationPath);
throw e;
}
// Removes scripts from package.json after build
if (pkg.keepLifecycleScripts !== true) {
log.info(`Removing scripts section in package.json as it's considered a potential security vulnerability.`);
delete packageJson.scripts;
} else {
log.warn(
`You enabled keepLifecycleScripts explicitly. The scripts section in package.json will be published to npm.`
);
}
// keep the dist package.json clean
// this will not throw if ngPackage field does not exist
delete packageJson.ngPackage;
packageJson.name = entryPoint.moduleId;
// `outputJson()` creates intermediate directories, if they do not exist
// -- https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputJson.md
await fs.outputJson(path.join(entryPoint.destinationPath, 'package.json'), packageJson, { spaces: 2 });
}
function checkNonPeerDependencies(packageJson: { [key: string]: any }, property: string, whitelist: RegExp[]) {
if (packageJson[property]) {
Object.keys(packageJson[property]).forEach(dep => {
if (whitelist.find(regex => regex.test(dep))) {
log.debug(`Dependency ${dep} is whitelisted in '${property}'`);
} else {
log.warn(
`Distributing npm packages with '${property}' is not recommended. Please consider adding ${dep} to 'peerDependencies' or remove it from '${property}'.`
);
throw new Error(`Dependency ${dep} must be explicitly whitelisted.`);
}
});
}
}