Paul Gschwendtner f862536ec4 build: fix ts-api-guardian golden approval not working on windows (#36096)
Currently on Windows, it's not possible to approve goldens in
`ts-api-guardian`. This is because paths are resolved relatively
to the working directory. In Windows, golden files are resolved
to the actual workspace directory. The current logic tries to
compute a relative path to the runfile from the working directory.

This causes the file paths to have a lot of parent directory
path segments. Eventually, when joined with the build workspace
directory, the paths end up being incorrect. e.g.

```
fileName = ../../../../../../projects/angular/golden/<..>/common.d.ts`
outFile = BUILD_WORKSPACE_DIR + fileName;
```

To fix this, we no longer deal with confusing relative paths, but
instead always use absolute file system paths.

Additionally, this fixes that new goldens are generated at the wrong
location on all platforms.

PR Close #36096
2020-03-17 10:42:09 -07:00

94 lines
2.7 KiB
TypeScript

/**
* @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 {createPatch} from 'diff';
import * as fs from 'fs';
import * as path from 'path';
import {SerializationOptions, publicApi} from './serializer';
export {SerializationOptions, publicApi} from './serializer';
export function generateGoldenFile(
entrypoint: string, outFile: string, options: SerializationOptions = {}): void {
const output = publicApi(entrypoint, options);
ensureDirectory(path.dirname(outFile));
fs.writeFileSync(outFile, output);
}
export function verifyAgainstGoldenFile(
entrypoint: string, goldenFile: string, options: SerializationOptions = {}): string {
const actual = publicApi(entrypoint, options);
const expected = fs.existsSync(goldenFile) ? fs.readFileSync(goldenFile).toString() : '';
if (actual === expected) {
return '';
} else {
const displayFileName = path.relative(process.cwd(), goldenFile);
const patch = createPatch(displayFileName, expected, actual, 'Golden file', 'Generated API');
// Remove the header of the patch
const start = patch.indexOf('\n', patch.indexOf('\n') + 1) + 1;
return patch.substring(start);
}
}
function ensureDirectory(dir: string) {
if (!fs.existsSync(dir)) {
ensureDirectory(path.dirname(dir));
fs.mkdirSync(dir);
}
}
/**
* Determine if the provided path is a directory.
*/
function isDirectory(dirPath: string) {
try {
return fs.lstatSync(dirPath).isDirectory();
} catch {
return false;
}
}
/**
* Gets an array of paths to the typings files for each of the recursively discovered
* package.json
* files from the directory provided.
*/
export function discoverAllEntrypoints(dirPath: string) {
// Determine all of the package.json files
const packageJsons: string[] = [];
const entryPoints: string[] = [];
const findPackageJsonsInDir = (nextPath: string) => {
for (const file of fs.readdirSync(nextPath)) {
const fullPath = path.join(nextPath, file);
if (isDirectory(fullPath)) {
findPackageJsonsInDir(fullPath);
} else {
if (file === 'package.json') {
packageJsons.push(fullPath);
}
}
}
};
findPackageJsonsInDir(dirPath);
// Get all typings file locations from package.json files
for (const packageJson of packageJsons) {
const packageJsonObj = JSON.parse(fs.readFileSync(packageJson, {encoding: 'utf8'}));
const typings = packageJsonObj.typings;
if (typings) {
entryPoints.push(path.join(path.dirname(packageJson), typings));
}
}
return entryPoints;
}