Skip to content

Commit

Permalink
feat(jsii-diff): make assembly validation optional (#926)
Browse files Browse the repository at this point in the history
Assembly validation is taking the majority of the time of doing the
API compatibility check, and the assemblies are almost guaranteed to
be correct.

Disable validation by default, add an option to enable it.
  • Loading branch information
rix0rrr committed Nov 5, 2019
1 parent 3528ad0 commit e2c80f0
Showing 1 changed file with 18 additions and 11 deletions.
29 changes: 18 additions & 11 deletions packages/jsii-diff/bin/jsii-diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ async function main(): Promise<number> {
.option('experimental-errors', { alias: 'e', type: 'boolean', default: false, desc: 'Error on experimental API changes' })
.option('ignore-file', { alias: 'i', type: 'string', desc: 'Ignore API changes with keys from file (file may be missing)' })
.option('keys', { alias: 'k', type: 'boolean', default: false, desc: 'Show diagnostic suppression keys' })
.option('validate', { alias: 'd', type: 'boolean', default: false, desc: 'Validate the assemblies that are being loaded' })
.usage('$0 <original> [updated]', 'Compare two JSII assemblies.', args => args
.positional('original', {
description: 'Original assembly (file, package or "npm:package@version")',
Expand All @@ -36,14 +37,14 @@ async function main(): Promise<number> {
configureLog4js(argv.verbose);

LOG.debug(`Loading original assembly from ${(argv as any).original}`);
const loadOriginal = await loadAssembly((argv as any).original);
const loadOriginal = await loadAssembly((argv as any).original, argv);
if (!loadOriginal.success) {
process.stderr.write(`Could not load '${loadOriginal.resolved}': ${showDownloadFailure(loadOriginal.reason)}. Skipping analysis\n`);
return 0;
}

LOG.debug(`Loading updated assembly from ${(argv as any).updated}`);
const loadUpdated = await loadAssembly((argv as any).updated);
const loadUpdated = await loadAssembly((argv as any).updated, argv);
if (!loadUpdated.success) {
process.stderr.write(`Could not load '${loadUpdated.resolved}': ${showDownloadFailure(loadUpdated.reason)}. Skipping analysis\n`);
return 0;
Expand Down Expand Up @@ -82,29 +83,34 @@ async function main(): Promise<number> {
// Allow both npm:<package> (legacy) and npm://<package> (looks better)
const NPM_REGEX = /^npm:(\/\/)?/;


interface LoadOptions {
validate: boolean;
}

/**
* Load the indicated assembly from the given name
*
* Supports downloading from NPM as well as from file or directory.
*/
async function loadAssembly(requested: string): Promise<LoadAssemblyResult> {
async function loadAssembly(requested: string, options: LoadOptions): Promise<LoadAssemblyResult> {
let resolved = requested;
try {
if (NPM_REGEX.exec(requested)) {
let pkg = requested.replace(NPM_REGEX, '');
if (!pkg) { pkg = await loadPackageNameFromAssembly(); }
if (!pkg) { pkg = await loadPackageNameFromAssembly(options); }

resolved = `npm://${pkg}`;
if (!pkg.includes('@', 1)) { resolved += '@latest'; }

const download = await downloadNpmPackage(pkg, loadFromFilesystem);
const download = await downloadNpmPackage(pkg, f => loadFromFilesystem(f, options));
if (download.success) {
return { requested, resolved, success: true, assembly: download.result };
}
return { requested, resolved, success: false, reason: download.reason };
}
// We don't accept failure loading from the filesystem
return { requested, resolved, success: true, assembly: await loadFromFilesystem(requested) };
return { requested, resolved, success: true, assembly: await loadFromFilesystem(requested, options) };

} catch (e) {
// Prepend information about which assembly we've failed to load
Expand All @@ -125,25 +131,26 @@ async function loadAssembly(requested: string): Promise<LoadAssemblyResult> {
type LoadAssemblyResult = { requested: string, resolved: string }
& ({ success: true, assembly: reflect.Assembly } | { success: false, reason: DownloadFailure });

async function loadPackageNameFromAssembly(): Promise<string> {
async function loadPackageNameFromAssembly(options: LoadOptions): Promise<string> {
const JSII_ASSEMBLY_FILE = '.jsii';
if (!await fs.pathExists(JSII_ASSEMBLY_FILE)) {
throw new Error(`No NPM package name given and no ${JSII_ASSEMBLY_FILE} file in the current directory. Please specify a package name.`);
}
const module = spec.validateAssembly(await fs.readJSON(JSII_ASSEMBLY_FILE, { encoding: 'utf-8' }));
const contents = await fs.readJSON(JSII_ASSEMBLY_FILE, { encoding: 'utf-8' });
const module = options.validate ? spec.validateAssembly(contents) : contents as spec.Assembly;
if (!module.name) { throw new Error(`Could not find package in ${JSII_ASSEMBLY_FILE}`); }

return module.name;
}

async function loadFromFilesystem(name: string) {
async function loadFromFilesystem(name: string, options: LoadOptions) {
const stat = await fs.stat(name);

const ts = new reflect.TypeSystem();
if (stat.isDirectory()) {
return ts.loadModule(name);
return ts.loadModule(name, options);
}
return ts.loadFile(name);
return ts.loadFile(name, options);

}

Expand Down

0 comments on commit e2c80f0

Please sign in to comment.