@teambit/schema 1.0.1080 → 1.0.1082
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/dist/api-diff-compute.spec.d.ts +1 -0
- package/dist/api-diff-compute.spec.js +547 -0
- package/dist/api-diff-compute.spec.js.map +1 -0
- package/dist/mock/button-diff.json +4 -20
- package/dist/mock/button-schemas.json +6 -146
- package/dist/mock/default-export/index.d.ts +5 -0
- package/dist/mock/default-export/index.js +21 -0
- package/dist/mock/default-export/index.js.map +1 -0
- package/dist/mock/default-export/tsconfig.json +19 -0
- package/dist/{preview-1785267101230.js → preview-1785362864561.js} +4 -4
- package/dist/schema-diff.cmd.d.ts +2 -0
- package/dist/schema-diff.cmd.js +51 -6
- package/dist/schema-diff.cmd.js.map +1 -1
- package/dist/schema.graphql.js +11 -0
- package/dist/schema.graphql.js.map +1 -1
- package/dist/schema.main.runtime.d.ts +33 -1
- package/dist/schema.main.runtime.js +157 -9
- package/dist/schema.main.runtime.js.map +1 -1
- package/dist/schema.spec.js +37 -0
- package/dist/schema.spec.js.map +1 -1
- package/mock/button-diff.json +4 -20
- package/mock/button-schemas.json +6 -146
- package/mock/default-export/index.ts +9 -0
- package/mock/default-export/tsconfig.json +19 -0
- package/package.json +20 -20
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _chai() {
|
|
4
|
+
const data = require("chai");
|
|
5
|
+
_chai = function () {
|
|
6
|
+
return data;
|
|
7
|
+
};
|
|
8
|
+
return data;
|
|
9
|
+
}
|
|
10
|
+
function _semanticsEntities() {
|
|
11
|
+
const data = require("@teambit/semantics.entities.semantic-schema");
|
|
12
|
+
_semanticsEntities = function () {
|
|
13
|
+
return data;
|
|
14
|
+
};
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
function _componentId() {
|
|
18
|
+
const data = require("@teambit/component-id");
|
|
19
|
+
_componentId = function () {
|
|
20
|
+
return data;
|
|
21
|
+
};
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
function _semanticsEntities2() {
|
|
25
|
+
const data = require("@teambit/semantics.entities.semantic-schema-diff");
|
|
26
|
+
_semanticsEntities2 = function () {
|
|
27
|
+
return data;
|
|
28
|
+
};
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
function _schemaMain() {
|
|
32
|
+
const data = require("./schema.main.runtime");
|
|
33
|
+
_schemaMain = function () {
|
|
34
|
+
return data;
|
|
35
|
+
};
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
const loc = {
|
|
39
|
+
filePath: 'index.ts',
|
|
40
|
+
line: 0,
|
|
41
|
+
character: 0
|
|
42
|
+
};
|
|
43
|
+
const compId = _componentId().ComponentID.fromString('org.scope/button');
|
|
44
|
+
function makeVar(name, type) {
|
|
45
|
+
return new (_semanticsEntities().VariableLikeSchema)(loc, name, `${name}: ${type}`, new (_semanticsEntities().KeywordTypeSchema)(loc, type), false);
|
|
46
|
+
}
|
|
47
|
+
function makeSchema(exports, internals = []) {
|
|
48
|
+
const internalModules = internals.length > 0 ? [new (_semanticsEntities().ModuleSchema)(loc, [], internals)] : [];
|
|
49
|
+
return new (_semanticsEntities().APISchema)(loc, new (_semanticsEntities().ModuleSchema)(loc, exports, []), internalModules, compId);
|
|
50
|
+
}
|
|
51
|
+
function makeAssessor() {
|
|
52
|
+
const assessor = new (_semanticsEntities2().ImpactAssessor)();
|
|
53
|
+
assessor.registerDefaultRules(_semanticsEntities2().DEFAULT_IMPACT_RULES);
|
|
54
|
+
return assessor;
|
|
55
|
+
}
|
|
56
|
+
describe('computeAPIDiff', () => {
|
|
57
|
+
describe('availability short-circuit', () => {
|
|
58
|
+
const unavailable = {
|
|
59
|
+
available: false,
|
|
60
|
+
reason: 'NOT_BUILT'
|
|
61
|
+
};
|
|
62
|
+
const available = {
|
|
63
|
+
available: true
|
|
64
|
+
};
|
|
65
|
+
it('should not fabricate a diff when the base schema is unavailable', () => {
|
|
66
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([]), makeSchema([makeVar('foo', 'string')]), makeAssessor(), {
|
|
67
|
+
base: unavailable,
|
|
68
|
+
compare: available
|
|
69
|
+
});
|
|
70
|
+
(0, _chai().expect)(result.status).to.equal('BASE_UNAVAILABLE');
|
|
71
|
+
(0, _chai().expect)(result.hasChanges).to.be.false;
|
|
72
|
+
(0, _chai().expect)(result.changes).to.have.length(0);
|
|
73
|
+
(0, _chai().expect)(result.base).to.deep.equal(unavailable);
|
|
74
|
+
(0, _chai().expect)(result.compare).to.deep.equal(available);
|
|
75
|
+
});
|
|
76
|
+
it('should not fabricate a diff when the compare schema is unavailable', () => {
|
|
77
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([makeVar('foo', 'string')]), makeSchema([]), makeAssessor(), {
|
|
78
|
+
base: available,
|
|
79
|
+
compare: unavailable
|
|
80
|
+
});
|
|
81
|
+
(0, _chai().expect)(result.status).to.equal('COMPARE_UNAVAILABLE');
|
|
82
|
+
(0, _chai().expect)(result.hasChanges).to.be.false;
|
|
83
|
+
(0, _chai().expect)(result.changes).to.have.length(0);
|
|
84
|
+
});
|
|
85
|
+
it('should report UNAVAILABLE when both schemas are unavailable', () => {
|
|
86
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([]), makeSchema([]), makeAssessor(), {
|
|
87
|
+
base: unavailable,
|
|
88
|
+
compare: {
|
|
89
|
+
available: false,
|
|
90
|
+
reason: 'NO_EXTRACTOR'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
(0, _chai().expect)(result.status).to.equal('UNAVAILABLE');
|
|
94
|
+
(0, _chai().expect)(result.hasChanges).to.be.false;
|
|
95
|
+
(0, _chai().expect)(result.compare.reason).to.equal('NO_EXTRACTOR');
|
|
96
|
+
});
|
|
97
|
+
it('should default to available and report COMPUTED', () => {
|
|
98
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(makeSchema([]), makeSchema([makeVar('foo', 'string')]), makeAssessor());
|
|
99
|
+
(0, _chai().expect)(result.status).to.equal('COMPUTED');
|
|
100
|
+
(0, _chai().expect)(result.base.available).to.be.true;
|
|
101
|
+
(0, _chai().expect)(result.compare.available).to.be.true;
|
|
102
|
+
(0, _chai().expect)(result.hasChanges).to.be.true;
|
|
103
|
+
(0, _chai().expect)(result.added).to.equal(1);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('impact derivation', () => {
|
|
107
|
+
it('should derive impact from public changes only', () => {
|
|
108
|
+
// internal-only breaking change: internal var type narrowed string → number
|
|
109
|
+
const base = makeSchema([makeVar('Button', 'string')], [makeVar('helper', 'string')]);
|
|
110
|
+
const compare = makeSchema([makeVar('Button', 'string')], [makeVar('helper', 'number')]);
|
|
111
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
112
|
+
(0, _chai().expect)(result.publicChanges).to.have.length(0);
|
|
113
|
+
(0, _chai().expect)(result.internalChanges).to.have.length(1);
|
|
114
|
+
(0, _chai().expect)(result.impact).to.equal('PATCH');
|
|
115
|
+
(0, _chai().expect)(result.internalImpact).to.equal('BREAKING');
|
|
116
|
+
(0, _chai().expect)(result.hasChanges).to.be.true;
|
|
117
|
+
});
|
|
118
|
+
it('should reflect public breaking changes in impact', () => {
|
|
119
|
+
const base = makeSchema([makeVar('Button', 'string')]);
|
|
120
|
+
const compare = makeSchema([]);
|
|
121
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
122
|
+
(0, _chai().expect)(result.removed).to.equal(1);
|
|
123
|
+
(0, _chai().expect)(result.impact).to.equal('BREAKING');
|
|
124
|
+
(0, _chai().expect)(result.internalImpact).to.equal('PATCH');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('computeAPIDiff default/re-export aliases (extraction-inconsistency robustness)', () => {
|
|
129
|
+
// Repro of a real false positive: `class AttReact` + `export default AttReact`. Across two builds
|
|
130
|
+
// the default export was extracted inconsistently — an UnImplementedSchema one version, a
|
|
131
|
+
// `AttReact (default)` TypeRef alias the next — while only the docs changed. The diff must not
|
|
132
|
+
// report the default re-export alias as a newly added public API.
|
|
133
|
+
const classExport = () => new (_semanticsEntities().ExportSchema)(loc, 'AttReact', makeVar('AttReact', 'string'));
|
|
134
|
+
const defaultAsUnimplemented = () => new (_semanticsEntities().UnImplementedSchema)(loc, 'AttReact', 'Identifier');
|
|
135
|
+
const defaultAsTypeRefAlias = () => new (_semanticsEntities().ExportSchema)(loc, 'AttReact (default)', new (_semanticsEntities().TypeRefSchema)(loc, 'AttReact'), 'AttReact (default)');
|
|
136
|
+
it('should not report an added export when the default re-export alias changes representation', () => {
|
|
137
|
+
const base = makeSchema([classExport(), defaultAsUnimplemented()]);
|
|
138
|
+
const compare = makeSchema([classExport(), defaultAsTypeRefAlias()]);
|
|
139
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
140
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
141
|
+
(0, _chai().expect)(result.added).to.equal(0);
|
|
142
|
+
(0, _chai().expect)(result.publicChanges).to.have.lengthOf(0);
|
|
143
|
+
});
|
|
144
|
+
it('should drop a redundant default type-ref alias to an already-exported symbol', () => {
|
|
145
|
+
const base = makeSchema([classExport()]);
|
|
146
|
+
const compare = makeSchema([classExport(), defaultAsTypeRefAlias()]);
|
|
147
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
148
|
+
(0, _chai().expect)(result.added).to.equal(0);
|
|
149
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
150
|
+
});
|
|
151
|
+
it('should still report a genuinely new export alongside a redundant alias', () => {
|
|
152
|
+
const base = makeSchema([classExport()]);
|
|
153
|
+
const compare = makeSchema([classExport(), defaultAsTypeRefAlias(), makeVar('brandNew', 'string')]);
|
|
154
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
155
|
+
(0, _chai().expect)(result.added).to.equal(1);
|
|
156
|
+
(0, _chai().expect)(result.publicChanges.map(c => c.exportName)).to.include('brandNew');
|
|
157
|
+
(0, _chai().expect)(result.publicChanges.map(c => c.exportName)).to.not.include('AttReact (default)');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('computeAPIDiff unresolved exports (extraction gaps)', () => {
|
|
161
|
+
const unresolved = name => new (_semanticsEntities().UnImplementedSchema)(loc, name, 'Identifier');
|
|
162
|
+
it('should not report a removal when the export is merely unresolved on the compare side', () => {
|
|
163
|
+
const base = makeSchema([makeVar('Foo', 'string')]);
|
|
164
|
+
const compare = makeSchema([unresolved('Foo')]);
|
|
165
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
166
|
+
(0, _chai().expect)(result.removed).to.equal(0);
|
|
167
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
168
|
+
(0, _chai().expect)(result.unresolvedExports).to.include('Foo');
|
|
169
|
+
});
|
|
170
|
+
it('should not report an addition when the export was merely unresolved on the base side', () => {
|
|
171
|
+
const base = makeSchema([unresolved('Foo')]);
|
|
172
|
+
const compare = makeSchema([makeVar('Foo', 'string')]);
|
|
173
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
174
|
+
(0, _chai().expect)(result.added).to.equal(0);
|
|
175
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
176
|
+
(0, _chai().expect)(result.unresolvedExports).to.include('Foo');
|
|
177
|
+
});
|
|
178
|
+
it('should surface an export unresolved on both sides without diffing it', () => {
|
|
179
|
+
const base = makeSchema([unresolved('Foo'), makeVar('Bar', 'string')]);
|
|
180
|
+
const compare = makeSchema([unresolved('Foo'), makeVar('Bar', 'string')]);
|
|
181
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
182
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
183
|
+
(0, _chai().expect)(result.unresolvedExports).to.include('Foo');
|
|
184
|
+
});
|
|
185
|
+
it('should NOT surface a name that is unresolved on one side but resolved elsewhere (real export wins)', () => {
|
|
186
|
+
// mirrors AttReact: a class export + an unresolved default that shares the class name.
|
|
187
|
+
const base = makeSchema([makeVar('AttReact', 'string'), unresolved('AttReact')]);
|
|
188
|
+
const compare = makeSchema([makeVar('AttReact', 'string')]);
|
|
189
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
190
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
191
|
+
(0, _chai().expect)(result.unresolvedExports).to.have.lengthOf(0);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
describe('computeAPIDiff UnresolvedSchema placeholders (degraded extraction)', () => {
|
|
195
|
+
// Repro of a real false positive: one side's extraction degraded every re-export to an
|
|
196
|
+
// `UnresolvedSchema` placeholder (no structure, no doc) while the other side came from a healthy
|
|
197
|
+
// build. The diff reported every documented export as MODIFIED "documentation removed" labeled
|
|
198
|
+
// "Unresolved" instead of routing the name to `unresolvedExports`.
|
|
199
|
+
const docs = new (_semanticsEntities().DocSchema)(loc, '/** backend server interface. */', 'backend server interface.');
|
|
200
|
+
const documentedVar = name => new (_semanticsEntities().VariableLikeSchema)(loc, name, `${name}: string`, new (_semanticsEntities().KeywordTypeSchema)(loc, 'string'), false, docs);
|
|
201
|
+
it('should not diff a documented export against an UnresolvedSchema placeholder on the compare side', () => {
|
|
202
|
+
const base = makeSchema([documentedVar('BackendServer')]);
|
|
203
|
+
const compare = makeSchema([new (_semanticsEntities().UnresolvedSchema)(loc, 'BackendServer')]);
|
|
204
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
205
|
+
(0, _chai().expect)(result.modified).to.equal(0);
|
|
206
|
+
(0, _chai().expect)(result.removed).to.equal(0);
|
|
207
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
208
|
+
(0, _chai().expect)(result.unresolvedExports).to.include('BackendServer');
|
|
209
|
+
});
|
|
210
|
+
it('should not diff an UnresolvedSchema placeholder on the base side against a real export', () => {
|
|
211
|
+
const base = makeSchema([new (_semanticsEntities().UnresolvedSchema)(loc, 'BackendServer')]);
|
|
212
|
+
const compare = makeSchema([documentedVar('BackendServer')]);
|
|
213
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
214
|
+
(0, _chai().expect)(result.modified).to.equal(0);
|
|
215
|
+
(0, _chai().expect)(result.added).to.equal(0);
|
|
216
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
217
|
+
(0, _chai().expect)(result.unresolvedExports).to.include('BackendServer');
|
|
218
|
+
});
|
|
219
|
+
it('should handle an export-wrapped UnresolvedSchema like a bare one', () => {
|
|
220
|
+
const base = makeSchema([documentedVar('Middleware')]);
|
|
221
|
+
const compare = makeSchema([new (_semanticsEntities().ExportSchema)(loc, 'Middleware', new (_semanticsEntities().UnresolvedSchema)(loc, 'Middleware'))]);
|
|
222
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
223
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
224
|
+
(0, _chai().expect)(result.unresolvedExports).to.include('Middleware');
|
|
225
|
+
});
|
|
226
|
+
it('should suppress internal placeholder-vs-real pairs without surfacing them as unresolved exports', () => {
|
|
227
|
+
const base = makeSchema([], [documentedVar('helper')]);
|
|
228
|
+
const compare = makeSchema([], [new (_semanticsEntities().UnresolvedSchema)(loc, 'helper')]);
|
|
229
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
230
|
+
(0, _chai().expect)(result.internalChanges).to.have.lengthOf(0);
|
|
231
|
+
(0, _chai().expect)(result.hasChanges).to.equal(false);
|
|
232
|
+
// internal extraction gaps are suppressed, not reported as unresolved *exports*
|
|
233
|
+
(0, _chai().expect)(result.unresolvedExports).to.have.lengthOf(0);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe('computeAPIDiff visibility moves', () => {
|
|
237
|
+
it('should report a public→internal move as a public BREAKING change (drives impact, not internalImpact)', () => {
|
|
238
|
+
const base = makeSchema([makeVar('foo', 'string')], []);
|
|
239
|
+
const compare = makeSchema([], [makeVar('foo', 'string')]);
|
|
240
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
241
|
+
(0, _chai().expect)(result.publicChanges).to.have.length(1);
|
|
242
|
+
(0, _chai().expect)(result.publicChanges[0].changes?.[0]?.changeKind).to.equal('visibility-public-to-internal');
|
|
243
|
+
(0, _chai().expect)(result.impact).to.equal('BREAKING');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
describe('SchemaMain.getSchemaWithAvailability reason mapping', () => {
|
|
247
|
+
const succeedStatus = 'succeed';
|
|
248
|
+
const loggerStub = {
|
|
249
|
+
warn: () => {},
|
|
250
|
+
debug: () => {},
|
|
251
|
+
error: () => {},
|
|
252
|
+
info: () => {}
|
|
253
|
+
};
|
|
254
|
+
function makeComponent(buildStatus = 'pending') {
|
|
255
|
+
return {
|
|
256
|
+
id: _componentId().ComponentID.fromString('org.scope/button'),
|
|
257
|
+
buildStatus
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function makeSchemaMain({
|
|
261
|
+
disabled = false,
|
|
262
|
+
workspace = {},
|
|
263
|
+
env = {},
|
|
264
|
+
artifacts = []
|
|
265
|
+
} = {}) {
|
|
266
|
+
const envs = {
|
|
267
|
+
getEnv: () => ({
|
|
268
|
+
env
|
|
269
|
+
})
|
|
270
|
+
};
|
|
271
|
+
const builder = {
|
|
272
|
+
getArtifactsVinylByAspectAndTaskName: async () => artifacts
|
|
273
|
+
};
|
|
274
|
+
const config = {
|
|
275
|
+
defaultParser: 'typescript',
|
|
276
|
+
disabled
|
|
277
|
+
};
|
|
278
|
+
return new (_schemaMain().SchemaMain)(undefined, undefined, envs, config, builder, workspace, loggerStub, new (_semanticsEntities2().ImpactAssessor)());
|
|
279
|
+
}
|
|
280
|
+
it('should report DISABLED when schema extraction is disabled by config', async () => {
|
|
281
|
+
const schemaMain = makeSchemaMain({
|
|
282
|
+
disabled: true
|
|
283
|
+
});
|
|
284
|
+
const {
|
|
285
|
+
availability
|
|
286
|
+
} = await schemaMain.getSchemaWithAvailability(makeComponent());
|
|
287
|
+
(0, _chai().expect)(availability).to.deep.equal({
|
|
288
|
+
available: false,
|
|
289
|
+
reason: 'DISABLED'
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
it('should report NO_EXTRACTOR when the env has no schema extractor', async () => {
|
|
293
|
+
const schemaMain = makeSchemaMain({
|
|
294
|
+
env: {
|
|
295
|
+
name: 'stub-env'
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
const {
|
|
299
|
+
availability
|
|
300
|
+
} = await schemaMain.getSchemaWithAvailability(makeComponent());
|
|
301
|
+
(0, _chai().expect)(availability).to.deep.equal({
|
|
302
|
+
available: false,
|
|
303
|
+
reason: 'NO_EXTRACTOR'
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
it('should report FAILED when extraction throws and no artifact exists', async () => {
|
|
307
|
+
const env = {
|
|
308
|
+
name: 'stub-env',
|
|
309
|
+
getSchemaExtractor: () => ({
|
|
310
|
+
extract: async () => {
|
|
311
|
+
throw new Error('boom');
|
|
312
|
+
},
|
|
313
|
+
dispose: () => {}
|
|
314
|
+
})
|
|
315
|
+
};
|
|
316
|
+
const schemaMain = makeSchemaMain({
|
|
317
|
+
env
|
|
318
|
+
});
|
|
319
|
+
const {
|
|
320
|
+
availability
|
|
321
|
+
} = await schemaMain.getSchemaWithAvailability(makeComponent());
|
|
322
|
+
(0, _chai().expect)(availability).to.deep.equal({
|
|
323
|
+
available: false,
|
|
324
|
+
reason: 'FAILED'
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
it('should report NOT_BUILT for a built/scope component without a schema artifact', async () => {
|
|
328
|
+
const schemaMain = makeSchemaMain({
|
|
329
|
+
workspace: undefined
|
|
330
|
+
});
|
|
331
|
+
const {
|
|
332
|
+
availability
|
|
333
|
+
} = await schemaMain.getSchemaWithAvailability(makeComponent(succeedStatus));
|
|
334
|
+
(0, _chai().expect)(availability).to.deep.equal({
|
|
335
|
+
available: false,
|
|
336
|
+
reason: 'NOT_BUILT'
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
it('should report available when a schema artifact exists', async () => {
|
|
340
|
+
const schemaJson = _semanticsEntities().APISchema.empty(_componentId().ComponentID.fromString('org.scope/button')).toObject();
|
|
341
|
+
const artifacts = [{
|
|
342
|
+
contents: Buffer.from(JSON.stringify(schemaJson))
|
|
343
|
+
}];
|
|
344
|
+
const schemaMain = makeSchemaMain({
|
|
345
|
+
workspace: undefined,
|
|
346
|
+
artifacts
|
|
347
|
+
});
|
|
348
|
+
const {
|
|
349
|
+
schema,
|
|
350
|
+
availability
|
|
351
|
+
} = await schemaMain.getSchemaWithAvailability(makeComponent(succeedStatus));
|
|
352
|
+
(0, _chai().expect)(availability).to.deep.equal({
|
|
353
|
+
available: true
|
|
354
|
+
});
|
|
355
|
+
(0, _chai().expect)(schema.module.exports).to.have.length(0);
|
|
356
|
+
});
|
|
357
|
+
it('should rethrow extraction errors when alwaysRunExtractor is set', async () => {
|
|
358
|
+
const env = {
|
|
359
|
+
name: 'stub-env',
|
|
360
|
+
getSchemaExtractor: () => ({
|
|
361
|
+
extract: async () => {
|
|
362
|
+
throw new Error('boom');
|
|
363
|
+
},
|
|
364
|
+
dispose: () => {}
|
|
365
|
+
})
|
|
366
|
+
};
|
|
367
|
+
const schemaMain = makeSchemaMain({
|
|
368
|
+
env
|
|
369
|
+
});
|
|
370
|
+
try {
|
|
371
|
+
await schemaMain.getSchemaWithAvailability(makeComponent(), false, true);
|
|
372
|
+
_chai().expect.fail('expected getSchemaWithAvailability to rethrow');
|
|
373
|
+
} catch (err) {
|
|
374
|
+
(0, _chai().expect)(err.message).to.equal('boom');
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
it('should flag a live-extracted schema so version-keyed consumers do not persist it', async () => {
|
|
378
|
+
const env = {
|
|
379
|
+
name: 'stub-env',
|
|
380
|
+
getSchemaExtractor: () => ({
|
|
381
|
+
extract: async () => makeSchema([makeVar('Foo', 'string')]),
|
|
382
|
+
dispose: () => {}
|
|
383
|
+
})
|
|
384
|
+
};
|
|
385
|
+
const schemaMain = makeSchemaMain({
|
|
386
|
+
env
|
|
387
|
+
});
|
|
388
|
+
const {
|
|
389
|
+
availability
|
|
390
|
+
} = await schemaMain.getSchemaWithAvailability(makeComponent());
|
|
391
|
+
(0, _chai().expect)(availability.available).to.equal(true);
|
|
392
|
+
(0, _chai().expect)(availability.live).to.equal(true);
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
describe('SchemaMain.computeAPIDiff schema sourcing', () => {
|
|
396
|
+
const loggerStub = {
|
|
397
|
+
warn: () => {},
|
|
398
|
+
debug: () => {},
|
|
399
|
+
error: () => {},
|
|
400
|
+
info: () => {}
|
|
401
|
+
};
|
|
402
|
+
function makeDiffSchemaMain({
|
|
403
|
+
workspace,
|
|
404
|
+
env,
|
|
405
|
+
artifacts
|
|
406
|
+
}) {
|
|
407
|
+
const envs = {
|
|
408
|
+
getEnv: () => ({
|
|
409
|
+
env
|
|
410
|
+
})
|
|
411
|
+
};
|
|
412
|
+
const builder = {
|
|
413
|
+
getArtifactsVinylByAspectAndTaskName: async () => artifacts
|
|
414
|
+
};
|
|
415
|
+
const config = {
|
|
416
|
+
defaultParser: 'typescript',
|
|
417
|
+
disabled: false
|
|
418
|
+
};
|
|
419
|
+
const impactRuleSlot = {
|
|
420
|
+
values: () => []
|
|
421
|
+
};
|
|
422
|
+
return new (_schemaMain().SchemaMain)(undefined, impactRuleSlot, envs, config, builder, workspace, loggerStub, new (_semanticsEntities2().ImpactAssessor)());
|
|
423
|
+
}
|
|
424
|
+
const makeComp = idStr => ({
|
|
425
|
+
id: _componentId().ComponentID.fromString(idStr),
|
|
426
|
+
buildStatus: 'pending'
|
|
427
|
+
});
|
|
428
|
+
const artifactOf = schema => [{
|
|
429
|
+
contents: Buffer.from(JSON.stringify(schema.toObject()))
|
|
430
|
+
}];
|
|
431
|
+
it('should read the built artifact — not live-extract — for an unmodified checkout, and memoize', async () => {
|
|
432
|
+
// repro: a checked-out CI-built snap reports buildStatus "pending", which used to force a
|
|
433
|
+
// tsserver extraction pass per component per diff (minutes on a cold lane-compare) whose
|
|
434
|
+
// degraded output was then cached under the immutable snap pair.
|
|
435
|
+
let extracted = 0;
|
|
436
|
+
const env = {
|
|
437
|
+
name: 'stub-env',
|
|
438
|
+
getSchemaExtractor: () => ({
|
|
439
|
+
extract: async () => {
|
|
440
|
+
extracted += 1;
|
|
441
|
+
return makeSchema([]);
|
|
442
|
+
},
|
|
443
|
+
dispose: () => {}
|
|
444
|
+
})
|
|
445
|
+
};
|
|
446
|
+
const workspace = {
|
|
447
|
+
getIdIfExist: id => id,
|
|
448
|
+
isModified: async () => false
|
|
449
|
+
};
|
|
450
|
+
const schemaMain = makeDiffSchemaMain({
|
|
451
|
+
workspace,
|
|
452
|
+
env,
|
|
453
|
+
artifacts: artifactOf(makeSchema([]))
|
|
454
|
+
});
|
|
455
|
+
const result = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
|
|
456
|
+
(0, _chai().expect)(result?.status).to.equal('COMPUTED');
|
|
457
|
+
(0, _chai().expect)(extracted).to.equal(0);
|
|
458
|
+
(0, _chai().expect)(result?.base.live).to.equal(undefined);
|
|
459
|
+
|
|
460
|
+
// artifact-derived result is immutable even with buildStatus "pending" — memoized
|
|
461
|
+
const again = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
|
|
462
|
+
(0, _chai().expect)(again).to.equal(result);
|
|
463
|
+
});
|
|
464
|
+
it('should live-extract for a modified checkout and never memoize that result', async () => {
|
|
465
|
+
let extracted = 0;
|
|
466
|
+
const env = {
|
|
467
|
+
name: 'stub-env',
|
|
468
|
+
getSchemaExtractor: () => ({
|
|
469
|
+
extract: async () => {
|
|
470
|
+
extracted += 1;
|
|
471
|
+
return makeSchema([makeVar('Foo', 'string')]);
|
|
472
|
+
},
|
|
473
|
+
dispose: () => {}
|
|
474
|
+
})
|
|
475
|
+
};
|
|
476
|
+
const workspace = {
|
|
477
|
+
getIdIfExist: id => id,
|
|
478
|
+
isModified: async () => true
|
|
479
|
+
};
|
|
480
|
+
const schemaMain = makeDiffSchemaMain({
|
|
481
|
+
workspace,
|
|
482
|
+
env,
|
|
483
|
+
artifacts: artifactOf(makeSchema([]))
|
|
484
|
+
});
|
|
485
|
+
const result = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
|
|
486
|
+
(0, _chai().expect)(result?.status).to.equal('COMPUTED');
|
|
487
|
+
(0, _chai().expect)(extracted).to.equal(2); // both sides extracted live, artifact bypassed
|
|
488
|
+
(0, _chai().expect)(result?.base.live).to.equal(true);
|
|
489
|
+
(0, _chai().expect)(result?.compare.live).to.equal(true);
|
|
490
|
+
const again = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
|
|
491
|
+
(0, _chai().expect)(again).to.not.equal(result); // live results are recomputed, never served from memo
|
|
492
|
+
(0, _chai().expect)(extracted).to.equal(4);
|
|
493
|
+
});
|
|
494
|
+
it('should read the artifact for a version that is not checked out in the workspace', async () => {
|
|
495
|
+
let extracted = 0;
|
|
496
|
+
const env = {
|
|
497
|
+
name: 'stub-env',
|
|
498
|
+
getSchemaExtractor: () => ({
|
|
499
|
+
extract: async () => {
|
|
500
|
+
extracted += 1;
|
|
501
|
+
return makeSchema([]);
|
|
502
|
+
},
|
|
503
|
+
dispose: () => {}
|
|
504
|
+
})
|
|
505
|
+
};
|
|
506
|
+
// checked out at a different version than either diff side
|
|
507
|
+
const workspace = {
|
|
508
|
+
getIdIfExist: id => id.changeVersion('9.9.9'),
|
|
509
|
+
isModified: async () => true
|
|
510
|
+
};
|
|
511
|
+
const schemaMain = makeDiffSchemaMain({
|
|
512
|
+
workspace,
|
|
513
|
+
env,
|
|
514
|
+
artifacts: artifactOf(makeSchema([]))
|
|
515
|
+
});
|
|
516
|
+
const result = await schemaMain.computeAPIDiff(makeComp('org.scope/button@0.0.1'), makeComp('org.scope/button@0.0.2'));
|
|
517
|
+
(0, _chai().expect)(result?.status).to.equal('COMPUTED');
|
|
518
|
+
(0, _chai().expect)(extracted).to.equal(0);
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
describe('computeAPIDiff self-referential returnType (degraded extraction)', () => {
|
|
522
|
+
// Repro of a real artifact: a component fn `Checkbox` extracted while React types were unresolved.
|
|
523
|
+
// quickinfo (and thus the stored signature) said `…): any`, but the extractor's definition-hunt
|
|
524
|
+
// resolved the function's own name and stored returnType = TypeRefSchema('Checkbox'). The diff
|
|
525
|
+
// must trust the signature in that case — showing "React.JSX.Element → Checkbox" while the
|
|
526
|
+
// signature snippet right above it shows "…): any" reads as nonsense.
|
|
527
|
+
const makeFn = (returnType, signatureReturn) => new (_semanticsEntities().FunctionLikeSchema)(loc, 'Checkbox', [], returnType, `function Checkbox(props: CheckboxProps): ${signatureReturn}`, []);
|
|
528
|
+
it('should prefer the signature return annotation when the returnType node is a self-reference', () => {
|
|
529
|
+
const base = makeSchema([makeFn(new (_semanticsEntities().InferenceTypeSchema)(loc, 'React.JSX.Element'), 'React.JSX.Element')]);
|
|
530
|
+
const compare = makeSchema([makeFn(new (_semanticsEntities().TypeRefSchema)(loc, 'Checkbox'), 'any')]);
|
|
531
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
532
|
+
const fact = result.publicChanges[0]?.changes?.find(c => c.changeKind === 'return-type-changed');
|
|
533
|
+
(0, _chai().expect)(fact, 'expected a return-type-changed fact').to.exist;
|
|
534
|
+
(0, _chai().expect)(fact?.from).to.equal('React.JSX.Element');
|
|
535
|
+
(0, _chai().expect)(fact?.to).to.equal('any');
|
|
536
|
+
});
|
|
537
|
+
it('should keep the node rendering when a self-named return type agrees with the signature', () => {
|
|
538
|
+
// legit pattern: function merged with a same-named interface, genuinely returning `Checkbox`.
|
|
539
|
+
const base = makeSchema([makeFn(new (_semanticsEntities().InferenceTypeSchema)(loc, 'React.JSX.Element'), 'React.JSX.Element')]);
|
|
540
|
+
const compare = makeSchema([makeFn(new (_semanticsEntities().TypeRefSchema)(loc, 'Checkbox'), 'Checkbox')]);
|
|
541
|
+
const result = (0, _semanticsEntities2().computeAPIDiff)(base, compare, makeAssessor());
|
|
542
|
+
const fact = result.publicChanges[0]?.changes?.find(c => c.changeKind === 'return-type-changed');
|
|
543
|
+
(0, _chai().expect)(fact?.to).to.equal('Checkbox');
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
//# sourceMappingURL=api-diff-compute.spec.js.map
|