jsii-diff 1.79.0 → 1.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,61 +2,84 @@
2
2
 
3
3
  __jsii-diff__ compares two jsii assemblies for compatibility.
4
4
 
5
- In the future, it will be able to do generic comparisons, but for
5
+ In the future, it will be able to do generic comparisons.
6
+ But for
6
7
  now it will compare assemblies for API compatibility, and exit
7
- with a non-zero exit code if any **stable** APIs have had incompatible
8
- changes.
8
+ with a non-zero exit code if any __stable__ or __deprecated__ APIs have had incompatible changes.
9
9
 
10
- API items that have no stability are treated as **stable**. To treat
11
- unmarked API items as experimental, pass the `--default-experimental` flag.
10
+ API items that have no stability are treated as __stable__.
11
+ To treat unmarked API items as experimental, pass the `--default-experimental` flag.
12
12
 
13
13
  ## Usage
14
14
 
15
15
  To compare two JSII packages:
16
16
 
17
- jsii-diff <old> [new]
17
+ ```console
18
+ jsii-diff <old> [new]
19
+ ```
18
20
 
19
21
  Packages can be identified by either:
20
22
 
21
- * **A path**, in which case it should be the path to a JSII package directory,
23
+ * __A path__, in which case it should be the path to a JSII package directory,
22
24
  or to a `.jsii` file.
23
- * **An NPM package specifier** of the form `npm:[<package>[@version]]`, in
25
+ * __An NPM package specifier__ of the form `npm:[<package>[@version]]`, in
24
26
  which case the indicated version is downloaded and used. If `@version` is
25
27
  left out, the latest version will be used. If `package` is left out,
26
28
  the assembly name of `.jsii` in the current directory will be used.
27
29
 
28
30
  To compare current package against latest published NPM release:
29
31
 
30
- jsii-diff npm:
32
+ ```console
33
+ jsii-diff npm:<package>
34
+ ```
35
+
36
+ ### Stability Error Classes
37
+
38
+ By default only incompatible changes to `stable` or `deprecated` APIs are treated as errors and will fail the command.
39
+ Changes to `experimental` or `external` APIs emit a warning.
40
+
41
+ Change this behavior with the `--error-on` flag:
42
+
43
+ ```console
44
+ jsii-diff npm:<package> --error-on=all
45
+ ```
46
+
47
+ The following `--error-on` groups are available:
48
+
49
+ | `--error-on` | Stabilities that cause an ERROR |
50
+ | ------------------ | -------------------------------------------------- |
51
+ | `prod` (default) | `stable`, `deprecated` |
52
+ | `non-experimental` | `stable`, `deprecated`, `external` |
53
+ | `all` | `stable`, `deprecated`, `experimental`, `external` |
31
54
 
32
55
  ## Details
33
56
 
34
- __jsii-diff__ will assert that code written against version **A** of a library
35
- will still typecheck when compiled against version **B** of that library. It
57
+ __jsii-diff__ will assert that code written against version __A__ of a library
58
+ will still typecheck when compiled against version __B__ of that library. It
36
59
  does this by verifying the following properties:
37
60
 
38
- - Any type (class/interface/enum) in **A** must also exist in **B**.
39
- - Enums have only added members.
40
- - Classes and interfaces have only added members, or modified existing
61
+ * Any type (class/interface/enum) in __A__ must also exist in __B__.
62
+ * Enums have only added members.
63
+ * Classes and interfaces have only added members, or modified existing
41
64
  members in an allowed way.
42
- - Property types are the same or have been strengthened (see below).
43
- - Methods have only added optional arguments, existing argument types have
65
+ * Property types are the same or have been strengthened (see below).
66
+ * Methods have only added optional arguments, existing argument types have
44
67
  only been weakened, and the return type has only been strengthened (see below).
45
68
 
46
69
  ### Strengthening and weakening
47
70
 
48
- - *Strengthening* a type refers to *excluding* more possible values. Changing
71
+ * *Strengthening* a type refers to *excluding* more possible values. Changing
49
72
  a field from `optional` to `required`, or changing a type from `any` to
50
73
  `string` are examples of strengthening.
51
74
 
52
- - As the opposite of strengthening, *weakening* refers to *allowing* more
75
+ * As the opposite of strengthening, *weakening* refers to *allowing* more
53
76
  possible values. Changing a field from `required` to `optional`, or
54
77
  changing a type to a superclass or interface are examples of weakening.
55
78
 
56
79
  An API can change in the following way without breaking its consumer:
57
80
 
58
- - It can *weaken* its input (require *less* from the caller); and
59
- - It can *strengthen* its output (guarantee *more* to the caller).
81
+ * It can *weaken* its input (require *less* from the caller); and
82
+ * It can *strengthen* its output (guarantee *more* to the caller).
60
83
 
61
84
  ### Struct types
62
85
 
@@ -64,12 +87,12 @@ Structs (interfaces consisting completely of `readonly` properties) are
64
87
  treated as bags of data. Their API compatibility will be evaluated depending
65
88
  on whether they appear in input or output position of operations.
66
89
 
67
- - Structs are *weakened* if all types of all of its properties are weakened.
90
+ * Structs are *weakened* if all types of all of its properties are weakened.
68
91
  Normally removing properties would also be considered weakening, but
69
92
  because that may cause references to the fields in existing code bases to
70
93
  become undefined (which is not allowed in most programming languages) we
71
94
  disallow removing properties.
72
- - Structs are *strengthened* if all types of all of its properties are
95
+ * Structs are *strengthened* if all types of all of its properties are
73
96
  strengthened, or if fields are added.
74
97
 
75
98
  __jsii-diff__ will check the evolution of structs against their position
package/bin/jsii-diff.js CHANGED
@@ -32,6 +32,13 @@ async function main() {
32
32
  type: 'boolean',
33
33
  default: false,
34
34
  desc: 'Error on experimental API changes',
35
+ deprecate: 'Use `--error-on` instead',
36
+ })
37
+ .option('error-on', {
38
+ type: 'string',
39
+ default: 'prod',
40
+ choices: diagnostics_1.ERROR_CLASSES,
41
+ desc: 'Which type of API changes should be treated as an error',
35
42
  })
36
43
  .option('ignore-file', {
37
44
  alias: 'i',
@@ -86,9 +93,9 @@ async function main() {
86
93
  });
87
94
  LOG.info(`Found ${mismatches.count} issues`);
88
95
  if (mismatches.count > 0) {
89
- const diags = (0, diagnostics_1.classifyDiagnostics)(mismatches, argv['experimental-errors'], await loadFilter(argv['ignore-file']));
96
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mismatches, (0, diagnostics_1.treatAsError)(argv['error-on'], argv['experimental-errors']), await loadFilter(argv['ignore-file']));
90
97
  process.stderr.write(`Original assembly: ${original.name}@${original.version}\n`);
91
- process.stderr.write(`Updated assembly: ${updated.name}@${updated.version}\n`);
98
+ process.stderr.write(`Updated assembly: ${updated.name}@${updated.version}\n`);
92
99
  process.stderr.write('API elements with incompatible changes:\n');
93
100
  for (const diag of diags) {
94
101
  process.stderr.write(`${(0, diagnostics_1.formatDiagnostic)(diag, argv.keys)}\n`);
@@ -1,3 +1,4 @@
1
+ import { Stability } from '@jsii/spec';
1
2
  import { Mismatches } from './types';
2
3
  export declare enum DiagLevel {
3
4
  Error = 0,
@@ -11,8 +12,14 @@ export interface Diagnostic {
11
12
  }
12
13
  export declare function formatDiagnostic(diag: Diagnostic, includeSuppressionKey?: boolean): string;
13
14
  export declare function hasErrors(diags: Diagnostic[]): boolean;
15
+ export declare function onlyErrors(diags: Diagnostic[]): Diagnostic[];
16
+ export declare function onlyWarnings(diags: Diagnostic[]): Diagnostic[];
17
+ export declare const ERROR_CLASSES: readonly ["prod", "non-experimental", "all"];
18
+ export declare type ErrorClass = (typeof ERROR_CLASSES)[number];
19
+ export declare const ERROR_CLASSES_TO_STABILITIES: Record<ErrorClass, Stability[]>;
20
+ export declare function treatAsError(errorClass: ErrorClass, deprecatedExperimentalErrors?: boolean): Set<Stability>;
14
21
  /**
15
22
  * Classify API mismatches into a set of warnings and errors
16
23
  */
17
- export declare function classifyDiagnostics(mismatches: Mismatches, experimentalErrors: boolean, skipFilter: Set<string>): Diagnostic[];
24
+ export declare function classifyDiagnostics(mismatches: Mismatches, shouldError: Set<Stability>, skipFilter?: Set<string>): Diagnostic[];
18
25
  //# sourceMappingURL=diagnostics.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.classifyDiagnostics = exports.hasErrors = exports.formatDiagnostic = exports.DiagLevel = void 0;
3
+ exports.classifyDiagnostics = exports.treatAsError = exports.ERROR_CLASSES_TO_STABILITIES = exports.ERROR_CLASSES = exports.onlyWarnings = exports.onlyErrors = exports.hasErrors = exports.formatDiagnostic = exports.DiagLevel = void 0;
4
4
  const spec_1 = require("@jsii/spec");
5
5
  var DiagLevel;
6
6
  (function (DiagLevel) {
@@ -26,10 +26,44 @@ function hasErrors(diags) {
26
26
  return diags.some((diag) => diag.level === DiagLevel.Error);
27
27
  }
28
28
  exports.hasErrors = hasErrors;
29
+ function onlyErrors(diags) {
30
+ return diags.filter((diag) => diag.level === DiagLevel.Error);
31
+ }
32
+ exports.onlyErrors = onlyErrors;
33
+ function onlyWarnings(diags) {
34
+ return diags.filter((diag) => diag.level === DiagLevel.Warning);
35
+ }
36
+ exports.onlyWarnings = onlyWarnings;
37
+ exports.ERROR_CLASSES = ['prod', 'non-experimental', 'all'];
38
+ exports.ERROR_CLASSES_TO_STABILITIES = {
39
+ prod: [spec_1.Stability.Stable, spec_1.Stability.Deprecated],
40
+ 'non-experimental': [
41
+ spec_1.Stability.Stable,
42
+ spec_1.Stability.Deprecated,
43
+ spec_1.Stability.External,
44
+ ],
45
+ all: [
46
+ spec_1.Stability.Stable,
47
+ spec_1.Stability.Experimental,
48
+ spec_1.Stability.External,
49
+ spec_1.Stability.Deprecated,
50
+ ],
51
+ };
52
+ function treatAsError(errorClass, deprecatedExperimentalErrors = false) {
53
+ const shouldError = new Set();
54
+ for (const stability of exports.ERROR_CLASSES_TO_STABILITIES[errorClass]) {
55
+ shouldError.add(stability);
56
+ }
57
+ if (deprecatedExperimentalErrors) {
58
+ shouldError.add(spec_1.Stability.Experimental);
59
+ }
60
+ return shouldError;
61
+ }
62
+ exports.treatAsError = treatAsError;
29
63
  /**
30
64
  * Classify API mismatches into a set of warnings and errors
31
65
  */
32
- function classifyDiagnostics(mismatches, experimentalErrors, skipFilter) {
66
+ function classifyDiagnostics(mismatches, shouldError, skipFilter = new Set()) {
33
67
  const ret = mismatches.mismatches.map((mis) => ({
34
68
  level: level(mis),
35
69
  message: mis.message,
@@ -41,11 +75,7 @@ function classifyDiagnostics(mismatches, experimentalErrors, skipFilter) {
41
75
  if (skipFilter.has(mis.violationKey)) {
42
76
  return DiagLevel.Skipped;
43
77
  }
44
- if (mis.stability === spec_1.Stability.Stable ||
45
- (mis.stability === spec_1.Stability.Experimental && experimentalErrors)) {
46
- return DiagLevel.Error;
47
- }
48
- return DiagLevel.Warning;
78
+ return shouldError.has(mis.stability) ? DiagLevel.Error : DiagLevel.Warning;
49
79
  }
50
80
  }
51
81
  exports.classifyDiagnostics = classifyDiagnostics;
package/lib/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** The qualified version number for this JSII compiler. */
2
- export declare const VERSION = "1.79.0 (build b22f628)";
2
+ export declare const VERSION = "1.81.0 (build 80988b0)";
3
3
  //# sourceMappingURL=version.d.ts.map
package/lib/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
- // Generated at 2023-03-23T12:00:39Z by generate.sh
2
+ // Generated at 2023-05-10T16:34:26Z by generate.sh
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.VERSION = void 0;
5
5
  /** The qualified version number for this JSII compiler. */
6
- exports.VERSION = '1.79.0 (build b22f628)';
6
+ exports.VERSION = '1.81.0 (build 80988b0)';
7
7
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsii-diff",
3
- "version": "1.79.0",
3
+ "version": "1.81.0",
4
4
  "description": "Assembly comparison for jsii",
5
5
  "license": "Apache-2.0",
6
6
  "author": {
@@ -33,10 +33,10 @@
33
33
  "package": "package-js"
34
34
  },
35
35
  "dependencies": {
36
- "@jsii/check-node": "1.79.0",
37
- "@jsii/spec": "^1.79.0",
36
+ "@jsii/check-node": "1.81.0",
37
+ "@jsii/spec": "^1.81.0",
38
38
  "fs-extra": "^10.1.0",
39
- "jsii-reflect": "^1.79.0",
39
+ "jsii-reflect": "^1.81.0",
40
40
  "log4js": "^6.9.1",
41
41
  "yargs": "^16.2.0"
42
42
  },
@@ -44,7 +44,7 @@
44
44
  "@types/fs-extra": "^9.0.13",
45
45
  "@types/tar-fs": "^2.0.1",
46
46
  "jest-expect-message": "^1.1.3",
47
- "jsii": "^1.79.0",
48
- "jsii-build-tools": "^1.79.0"
47
+ "jsii": "^1.81.0",
48
+ "jsii-build-tools": "^1.81.0"
49
49
  }
50
50
  }
@@ -1,68 +1,106 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const spec_1 = require("@jsii/spec");
3
4
  const diagnostics_1 = require("../lib/diagnostics");
4
5
  const util_1 = require("./util");
5
6
  // ----------------------------------------------------------------------
6
- test('experimental elements lead to warnings', () => {
7
+ test('experimental stability violations lead to warnings', () => {
7
8
  const mms = (0, util_1.compare)(`
8
9
  /** @experimental */
9
10
  export class Foo1 { }
10
11
  `, `
11
- export class Foo2 { }
12
+ export class FooNew { }
12
13
  `);
13
- const experimentalErrors = false;
14
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set());
14
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('prod'));
15
15
  expect(diags.length).toBe(1);
16
16
  expect((0, diagnostics_1.hasErrors)(diags)).toBeFalsy();
17
17
  });
18
18
  // ----------------------------------------------------------------------
19
+ test('experimental stability violations can be turned into errors', () => {
20
+ const mms = (0, util_1.compare)(`
21
+ /** @experimental */
22
+ export class Foo1 { }
23
+ `, `
24
+ export class FooNew { }
25
+ `);
26
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('all'));
27
+ expect((0, diagnostics_1.onlyErrors)(diags).length).toBe(1);
28
+ expect((0, diagnostics_1.onlyWarnings)(diags).length).toBe(0);
29
+ expect((0, diagnostics_1.hasErrors)(diags)).toBeTruthy();
30
+ });
31
+ // ----------------------------------------------------------------------
19
32
  test('external stability violations are reported as warnings', () => {
20
33
  const mms = (0, util_1.compare)(`
21
34
  /** @stability external */
22
35
  export class Foo1 { }
36
+
23
37
  `, `
24
- export class Foo2 { }
38
+ export class FooNew { }
25
39
  `);
26
- const experimentalErrors = false;
27
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set());
40
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('prod'));
28
41
  expect(diags.length).toBe(1);
29
42
  expect((0, diagnostics_1.hasErrors)(diags)).toBeFalsy();
30
43
  });
31
44
  // ----------------------------------------------------------------------
32
- test('warnings can be turned into errors', () => {
45
+ test('external stability violations can be turned into errors', () => {
33
46
  const mms = (0, util_1.compare)(`
34
- /** @experimental */
47
+ /** @stability external */
35
48
  export class Foo1 { }
49
+
50
+ /** @stability experimental */
51
+ export class Foo2 { }
36
52
  `, `
37
- export class Foo2 { }
53
+ export class FooNew { }
38
54
  `);
39
- const experimentalErrors = true;
40
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set());
41
- expect(diags.length).toBe(1);
55
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('non-experimental'));
56
+ expect((0, diagnostics_1.onlyErrors)(diags).length).toBe(1);
57
+ expect((0, diagnostics_1.onlyWarnings)(diags).length).toBe(1);
42
58
  expect((0, diagnostics_1.hasErrors)(diags)).toBeTruthy();
43
59
  });
44
60
  // ----------------------------------------------------------------------
45
- test('external stability violations are never turned into errors', () => {
61
+ test('deprecated stability violations are reported as errors', () => {
46
62
  const mms = (0, util_1.compare)(`
47
- /** @stability external */
63
+ /** @deprecated for some reason */
48
64
  export class Foo1 { }
65
+
49
66
  `, `
50
- export class Foo2 { }
67
+ export class FooNew { }
51
68
  `);
52
- const experimentalErrors = true;
53
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set());
69
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('prod'));
54
70
  expect(diags.length).toBe(1);
55
- expect((0, diagnostics_1.hasErrors)(diags)).toBeFalsy();
71
+ expect((0, diagnostics_1.hasErrors)(diags)).toBeTruthy();
72
+ });
73
+ // ----------------------------------------------------------------------
74
+ describe('treatAsError', () => {
75
+ test.each([
76
+ ['prod', [spec_1.Stability.Deprecated, spec_1.Stability.Stable]],
77
+ [
78
+ 'all',
79
+ [
80
+ spec_1.Stability.Deprecated,
81
+ spec_1.Stability.Experimental,
82
+ spec_1.Stability.External,
83
+ spec_1.Stability.Stable,
84
+ ],
85
+ ],
86
+ [
87
+ 'non-experimental',
88
+ [spec_1.Stability.Deprecated, spec_1.Stability.External, spec_1.Stability.Stable],
89
+ ],
90
+ ])('%s', (errorClasses, expectedStabilities) => {
91
+ const shouldError = (0, diagnostics_1.treatAsError)(errorClasses);
92
+ expect(shouldError.size).toBe(expectedStabilities.length);
93
+ expect([...expectedStabilities].every((s) => shouldError.has(s))).toBe(true);
94
+ });
56
95
  });
57
96
  // ----------------------------------------------------------------------
58
- test('errors can be skipped', () => {
97
+ test('errors can be skipped by key', () => {
59
98
  const mms = (0, util_1.compare)(`
60
99
  export class Foo1 { }
61
100
  `, `
62
101
  export class Foo2 { }
63
102
  `);
64
- const experimentalErrors = true;
65
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set([mms.mismatches[0].violationKey]));
103
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('prod'), new Set([mms.mismatches[0].violationKey]));
66
104
  expect(diags.length).toBe(1);
67
105
  expect((0, diagnostics_1.hasErrors)(diags)).toBeFalsy();
68
106
  });
@@ -75,8 +113,7 @@ test('changing stable to experimental is breaking', () => {
75
113
  /** @experimental */
76
114
  export class Foo1 { }
77
115
  `);
78
- const experimentalErrors = false;
79
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set());
116
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('prod'));
80
117
  expect(diags.length).toBeGreaterThan(0);
81
118
  expect(diags.some((d) => /stability not allowed to go from 'stable' to 'experimental'/.exec(d.message))).toBeTruthy();
82
119
  expect((0, diagnostics_1.hasErrors)(diags)).toBeTruthy();
@@ -102,8 +139,7 @@ test('can make fields optional in output struct if it is marked @external', () =
102
139
  foo(): TheStruct;
103
140
  }
104
141
  `);
105
- const experimentalErrors = true;
106
- const diags = (0, diagnostics_1.classifyDiagnostics)(mms, experimentalErrors, new Set());
142
+ const diags = (0, diagnostics_1.classifyDiagnostics)(mms, (0, diagnostics_1.treatAsError)('prod'));
107
143
  expect(diags.length).toBe(1);
108
144
  expect((0, diagnostics_1.hasErrors)(diags)).toBeFalsy();
109
145
  });