-
Notifications
You must be signed in to change notification settings - Fork 390
/
index.ts
153 lines (135 loc) · 4.72 KB
/
index.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
146
147
148
149
150
151
152
153
import {
BuilderContext,
BuilderOutput,
createBuilder,
} from '@angular-devkit/architect';
import { NgPackagrBuilderOptions } from '@angular-devkit/build-angular';
import { JsonObject, logging } from '@angular-devkit/core';
import { promises as fs } from 'fs';
import * as globModule from 'glob';
import * as path from 'path';
import { from, Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { promisify } from 'util';
const glob = promisify(globModule);
const DELIMITER_START = '/** AUGMENTABLE_TYPES_START */';
const DELIMITER_END = '/** AUGMENTABLE_TYPES_END */';
export default createBuilder(augmentedTypesBuilder);
/**
* Builder that runs default ng-packagr builder ('@angular-devkit/build-angular:ng-packagr')
* and performs additional post step to move augmentable types to root d.ts file.
*
* It's a workaround to make TS types augmentable, reference issues:
* - https://github.com/microsoft/TypeScript/issues/9532
* - https://github.com/microsoft/TypeScript/issues/18877
*/
function augmentedTypesBuilder(
options: NgPackagrBuilderOptions & JsonObject,
context: BuilderContext
): Observable<BuilderOutput> {
return from(ngPackagrBuild(context, options)).pipe(
switchMap((result) =>
result.success
? from(
augmentableTypesPostStep(
context,
options as NgPackagrBuilderOptions
)
)
: of(result)
)
);
}
/**
* Run ng packager build step as is
*/
async function ngPackagrBuild(
context: BuilderContext,
options: NgPackagrBuilderOptions & JsonObject
): Promise<BuilderOutput> {
const builderRun = await context.scheduleBuilder(
'@angular-devkit/build-angular:ng-packagr',
options,
{ target: context.target }
);
return await builderRun.result;
}
/**
* Post build step
*/
async function augmentableTypesPostStep(
context: BuilderContext,
options: NgPackagrBuilderOptions
): Promise<BuilderOutput> {
const outputPath = await getNgPackgrLibOutputPath(options.project);
await propagateAugmentableTypes(outputPath, context.logger);
return { success: true };
}
/**
* Get output directory for ng packager job
* @param ngPackagerFile
*/
async function getNgPackgrLibOutputPath(ngPackagerFile: string) {
let ngPackageData = JSON.parse(await fs.readFile(ngPackagerFile, 'utf8'));
return path.join(path.dirname(ngPackagerFile), ngPackageData.dest);
}
/**
* Propagate augmentable types for every package.json file in the built in library
*/
async function propagateAugmentableTypes(
libPath: string,
logger: logging.LoggerApi
) {
// grab all package.json files
const files = await glob(libPath + '/**/package.json');
for (const packageJsonFile of files) {
try {
// get typings file from package.json
let packageData = JSON.parse(await fs.readFile(packageJsonFile, 'utf8'));
const typingsFile = packageData.typings;
if (!typingsFile) {
continue;
}
const packageJsonDir = path.dirname(packageJsonFile);
const typingsFilePath = path.join(packageJsonDir, typingsFile);
let typingsFileSource = await fs.readFile(typingsFilePath, 'utf8');
// look for export from public api file
const regex = /export \* from '(.+)\'/;
const publicApiFile = typingsFileSource.match(regex)![1];
const apiFilePath = path.join(packageJsonDir, publicApiFile + '.d.ts');
let publicApiFileSource = await fs.readFile(apiFilePath, 'utf8');
// find augmentable types delimiter in public api file
const augTypesStart = publicApiFileSource.indexOf(DELIMITER_START);
if (augTypesStart === -1) {
continue;
}
const augTypesEnd =
publicApiFileSource.indexOf(DELIMITER_END) + DELIMITER_END.length + 1;
// extract augmentable types block
const augTypes = publicApiFileSource.substr(
augTypesStart,
augTypesEnd - augTypesStart
);
// remove augmentable types block from public api file
publicApiFileSource =
publicApiFileSource.substr(0, augTypesStart) +
publicApiFileSource.substr(augTypesEnd);
// incorporate augmentable types block into typings file
const firstExportPos = typingsFileSource.indexOf('export *');
typingsFileSource =
typingsFileSource.substr(0, firstExportPos) +
augTypes +
typingsFileSource.substr(firstExportPos);
// write results
await fs.writeFile(apiFilePath, publicApiFileSource, 'utf8');
await fs.writeFile(typingsFilePath, typingsFileSource, 'utf8');
logger.info(
'Propagated types from ' + apiFilePath + ' to ' + typingsFilePath
);
} catch (e) {
logger.error(
'Error when propagating augmentable types for ' + packageJsonFile
);
}
}
}