angular-compat-matrix 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +126 -2
- package/dist/app.js +90 -0
- package/dist/build.js +46 -0
- package/dist/cli.js +210 -0
- package/dist/exec.js +110 -0
- package/dist/install.js +131 -0
- package/dist/library.js +184 -0
- package/dist/model.js +12 -0
- package/dist/partial.js +90 -0
- package/dist/peers.js +79 -0
- package/dist/registry.js +85 -0
- package/dist/report.js +233 -0
- package/dist/run.js +200 -0
- package/dist/smoke.js +524 -0
- package/dist/tests.js +100 -0
- package/dist/tools.js +86 -0
- package/dist/versions.js +138 -0
- package/package.json +49 -4
package/dist/report.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { CHECK_LABELS, CHECKS } from "./model.js";
|
|
2
|
+
const FAILING_CHECKS = ['install', 'partial', 'aot', 'strict', 'tests'];
|
|
3
|
+
function firstFailure(version) {
|
|
4
|
+
for (const name of FAILING_CHECKS) {
|
|
5
|
+
if (version.checks[name].status === 'fail')
|
|
6
|
+
return [name, version.checks[name]];
|
|
7
|
+
}
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
/** Everything that ran, passed. */
|
|
11
|
+
function works(version) {
|
|
12
|
+
return !version.setupError && !firstFailure(version);
|
|
13
|
+
}
|
|
14
|
+
/** CI goes red only for broken promises (2 means ng-matrix itself couldn't check a version). */
|
|
15
|
+
export function exitCode(versions) {
|
|
16
|
+
if (versions.some((v) => v.setupError))
|
|
17
|
+
return 2;
|
|
18
|
+
return versions.some((v) => v.claimed === true && firstFailure(v)) ? 1 : 0;
|
|
19
|
+
}
|
|
20
|
+
export function findings(versions, manifest,
|
|
21
|
+
/** The `@angular/*` packages the published code imports. */
|
|
22
|
+
angularImports = ['@angular/core']) {
|
|
23
|
+
const raw = [];
|
|
24
|
+
for (const version of versions) {
|
|
25
|
+
const m = version.major;
|
|
26
|
+
if (version.setupError) {
|
|
27
|
+
raw.push({ level: 'error', majors: [m], key: `setup:${version.setupError}`, text: `ng-matrix couldn't set up the app: ${version.setupError}` });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const failure = firstFailure(version);
|
|
31
|
+
if (failure) {
|
|
32
|
+
const [name, check] = failure;
|
|
33
|
+
// Partial's message names the declaration; a build's message is just "failed", so its hint says more.
|
|
34
|
+
const why = name === 'partial' && check.hint ? `${check.message} ${check.hint}` : (check.hint ?? check.message);
|
|
35
|
+
if (version.claimed === true) {
|
|
36
|
+
raw.push({ level: 'error', majors: [m], key: `fail:${name}:${why}`, text: `${CHECK_LABELS[name]}: ${why}` });
|
|
37
|
+
}
|
|
38
|
+
else if (version.claimed === false) {
|
|
39
|
+
raw.push({
|
|
40
|
+
level: 'note',
|
|
41
|
+
majors: [m],
|
|
42
|
+
key: `outside:${name}`,
|
|
43
|
+
text: `${CHECK_LABELS[name]} fails, but the peer range doesn't claim this version, so that's expected.`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
raw.push({ level: 'warning', majors: [m], key: `fail:${name}:${why}`, text: `${CHECK_LABELS[name]}: ${why}` });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (version.claimed === false) {
|
|
51
|
+
const ranges = version.peerMisses.map((miss) => `${miss.name} "${miss.range}"`).join(', ');
|
|
52
|
+
raw.push({
|
|
53
|
+
level: 'warning',
|
|
54
|
+
majors: [m],
|
|
55
|
+
key: `unclaimed:${ranges}`,
|
|
56
|
+
text: `works, but peerDependencies doesn't allow it (${ranges}). Widen the range to include it.`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// One finding per distinct problem, listing every major it applies to.
|
|
61
|
+
const merged = [];
|
|
62
|
+
const byKey = new Map();
|
|
63
|
+
for (const finding of raw) {
|
|
64
|
+
const existing = byKey.get(finding.key);
|
|
65
|
+
if (existing)
|
|
66
|
+
existing.majors.push(...finding.majors);
|
|
67
|
+
else {
|
|
68
|
+
const copy = { level: finding.level, majors: [...finding.majors], text: finding.text };
|
|
69
|
+
byKey.set(finding.key, copy);
|
|
70
|
+
merged.push(copy);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const hasAngularPeer = Object.keys(manifest.peerDependencies ?? {}).some((n) => n.startsWith('@angular/'));
|
|
74
|
+
if (!hasAngularPeer)
|
|
75
|
+
merged.push(missingPeersFinding(versions, angularImports));
|
|
76
|
+
const order = { error: 0, warning: 1, note: 2 };
|
|
77
|
+
return merged.sort((a, b) => order[a.level] - order[b.level]);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A package without `@angular/*` peers claims nothing, so nothing tells a package manager which
|
|
81
|
+
* Angular it needs. The hint suggests a range from the majors that passed every check, for each
|
|
82
|
+
* Angular package the published code imports.
|
|
83
|
+
*/
|
|
84
|
+
function missingPeersFinding(versions, angularImports) {
|
|
85
|
+
const working = versions.filter((v) => works(v) && !v.prerelease).map((v) => v.major);
|
|
86
|
+
const text = "The package declares no @angular/* peer dependency, so it claims no version and package managers can't warn about a mismatch.";
|
|
87
|
+
if (!working.length)
|
|
88
|
+
return { level: 'warning', majors: [], text: `${text} No version passed in this run, so there's no range to suggest.` };
|
|
89
|
+
const range = [...new Set(working)].sort((a, b) => a - b).map((m) => `^${m}.0.0`).join(' || ');
|
|
90
|
+
const packages = [...new Set(['@angular/core', ...angularImports])].sort();
|
|
91
|
+
const suggested = Object.fromEntries(packages.map((name) => [name, range]));
|
|
92
|
+
const lines = packages.map((name) => `"${name}": "${range}"`).join(', ');
|
|
93
|
+
return {
|
|
94
|
+
level: 'warning',
|
|
95
|
+
majors: [],
|
|
96
|
+
text: `${text} From the versions that passed: ${lines}.`,
|
|
97
|
+
suggestedPeerDependencies: suggested,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function majorsText(majors) {
|
|
101
|
+
const sorted = [...new Set(majors)].sort((a, b) => a - b);
|
|
102
|
+
const runs = [];
|
|
103
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
104
|
+
let j = i;
|
|
105
|
+
while (j + 1 < sorted.length && sorted[j + 1] === sorted[j] + 1)
|
|
106
|
+
j++;
|
|
107
|
+
runs.push(j > i ? `${sorted[i]}–${sorted[j]}` : String(sorted[i]));
|
|
108
|
+
i = j;
|
|
109
|
+
}
|
|
110
|
+
return runs.join(', ');
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------- terminal
|
|
113
|
+
const SYMBOL = { pass: '✓', fail: '✗', 'not-run': '—', off: '', none: '·' };
|
|
114
|
+
function symbol(check) {
|
|
115
|
+
if (check.status === 'pass' && check.advisory)
|
|
116
|
+
return '✓?';
|
|
117
|
+
return SYMBOL[check.status];
|
|
118
|
+
}
|
|
119
|
+
function columns(report) {
|
|
120
|
+
return CHECKS.filter((c) => c !== 'tests' || report.versions.some((v) => v.checks.tests.status !== 'off'));
|
|
121
|
+
}
|
|
122
|
+
function rowLabel(version) {
|
|
123
|
+
return version.prerelease ? `Angular ${version.angularVersion}` : `Angular ${version.major}`;
|
|
124
|
+
}
|
|
125
|
+
function center(text, width) {
|
|
126
|
+
const length = [...text].length;
|
|
127
|
+
const left = Math.floor((width - length) / 2);
|
|
128
|
+
return ' '.repeat(Math.max(0, left)) + text + ' '.repeat(Math.max(0, width - length - left));
|
|
129
|
+
}
|
|
130
|
+
export function headline(report) {
|
|
131
|
+
const parts = [];
|
|
132
|
+
if (report.library.compiledWith)
|
|
133
|
+
parts.push(`compiled with Angular ${report.library.compiledWith}`);
|
|
134
|
+
if (report.library.highestMinVersion)
|
|
135
|
+
parts.push(`highest minVersion ${report.library.highestMinVersion}`);
|
|
136
|
+
return `${report.library.name}@${report.library.version}${parts.length ? ` (${parts.join('; ')})` : ''}`;
|
|
137
|
+
}
|
|
138
|
+
export function renderTerminal(report, verbose = false) {
|
|
139
|
+
const cols = columns(report);
|
|
140
|
+
const labelWidth = Math.max(...report.versions.map((v) => rowLabel(v).length), 10) + 2;
|
|
141
|
+
const widths = cols.map((c) => CHECK_LABELS[c].length + 2);
|
|
142
|
+
const lines = [headline(report), ''];
|
|
143
|
+
lines.push(' '.repeat(labelWidth) + cols.map((c, i) => center(CHECK_LABELS[c], widths[i])).join(''));
|
|
144
|
+
for (const version of report.versions) {
|
|
145
|
+
const cells = version.setupError
|
|
146
|
+
? cols.map((c, i) => center(c === 'peer' ? symbol(version.checks.peer) : '!', widths[i]))
|
|
147
|
+
: cols.map((c, i) => center(symbol(version.checks[c]), widths[i]));
|
|
148
|
+
lines.push(rowLabel(version).padEnd(labelWidth) + cells.join('').trimEnd());
|
|
149
|
+
}
|
|
150
|
+
if (report.versions.some((v) => v.checks.partial.advisory)) {
|
|
151
|
+
lines.push('', '✓? partial passed on minVersion alone, which has been too low before; the build decides.');
|
|
152
|
+
}
|
|
153
|
+
if (report.findings.length)
|
|
154
|
+
lines.push('');
|
|
155
|
+
for (const finding of report.findings) {
|
|
156
|
+
const mark = finding.level === 'error' ? '✗' : finding.level === 'warning' ? '!' : '·';
|
|
157
|
+
const where = finding.majors.length ? `${majorsText(finding.majors)}: ` : '';
|
|
158
|
+
lines.push(wrap(`${mark} ${where}${finding.text}`, 88, ' '));
|
|
159
|
+
}
|
|
160
|
+
const failing = report.versions.filter((v) => firstFailure(v) || v.setupError);
|
|
161
|
+
for (const version of failing) {
|
|
162
|
+
const failure = firstFailure(version);
|
|
163
|
+
const errors = version.setupError ? version.setupError.split('\n').slice(1) : (failure?.[1].errorLines ?? []);
|
|
164
|
+
if (!errors.length)
|
|
165
|
+
continue;
|
|
166
|
+
lines.push('', `${rowLabel(version)}, ${failure ? CHECK_LABELS[failure[0]] : 'setup'}:`);
|
|
167
|
+
for (const line of errors.slice(0, verbose ? errors.length : 6))
|
|
168
|
+
lines.push(` ${line.trim()}`);
|
|
169
|
+
}
|
|
170
|
+
lines.push('', exitSummary(report));
|
|
171
|
+
return lines.join('\n') + '\n';
|
|
172
|
+
}
|
|
173
|
+
function exitSummary(report) {
|
|
174
|
+
if (report.exitCode === 2)
|
|
175
|
+
return "Couldn't check every version; see above.";
|
|
176
|
+
if (report.exitCode === 1)
|
|
177
|
+
return 'A version the peer range claims is broken.';
|
|
178
|
+
return 'Every version the peer range claims works.';
|
|
179
|
+
}
|
|
180
|
+
function wrap(text, width, indent) {
|
|
181
|
+
const words = text.split(' ');
|
|
182
|
+
const lines = [];
|
|
183
|
+
let current = '';
|
|
184
|
+
for (const word of words) {
|
|
185
|
+
if (current && (current + ' ' + word).length > width) {
|
|
186
|
+
lines.push(current);
|
|
187
|
+
current = indent + word;
|
|
188
|
+
}
|
|
189
|
+
else
|
|
190
|
+
current = current ? `${current} ${word}` : word;
|
|
191
|
+
}
|
|
192
|
+
if (current)
|
|
193
|
+
lines.push(current);
|
|
194
|
+
return lines.join('\n');
|
|
195
|
+
}
|
|
196
|
+
// ---------------------------------------------------------------- markdown
|
|
197
|
+
const MD = { pass: '✅', fail: '❌', 'not-run': '—', off: '', none: 'n/a' };
|
|
198
|
+
export function renderMarkdown(report) {
|
|
199
|
+
const cols = columns(report);
|
|
200
|
+
const date = report.checkedAt.slice(0, 10);
|
|
201
|
+
const compiled = report.library.compiledWith ? `, compiled with Angular ${report.library.compiledWith}` : '';
|
|
202
|
+
const lines = [
|
|
203
|
+
'## Angular compatibility',
|
|
204
|
+
'',
|
|
205
|
+
`Checked on ${date} for \`${report.library.name}@${report.library.version}\`${compiled}, by installing the packed package into a fresh app of each version with ${report.packageManager} and building it ([angular-compat-matrix](https://github.com/berserker5619/angular-compat-matrix)).`,
|
|
206
|
+
'',
|
|
207
|
+
`| Angular | ${cols.map((c) => CHECK_LABELS[c][0].toUpperCase() + CHECK_LABELS[c].slice(1)).join(' | ')} |`,
|
|
208
|
+
`| :-- | ${cols.map(() => ':-:').join(' | ')} |`,
|
|
209
|
+
];
|
|
210
|
+
for (const version of report.versions) {
|
|
211
|
+
const cells = cols.map((c) => {
|
|
212
|
+
if (version.setupError && c !== 'peer')
|
|
213
|
+
return '⚠️';
|
|
214
|
+
const check = version.checks[c];
|
|
215
|
+
return check.status === 'pass' && check.advisory ? '✅*' : MD[check.status];
|
|
216
|
+
});
|
|
217
|
+
lines.push(`| ${version.prerelease ? version.angularVersion : `${version.major} (${version.angularVersion})`} | ${cells.join(' | ')} |`);
|
|
218
|
+
}
|
|
219
|
+
if (report.findings.length)
|
|
220
|
+
lines.push('');
|
|
221
|
+
for (const finding of report.findings) {
|
|
222
|
+
const mark = finding.level === 'error' ? '❌' : finding.level === 'warning' ? '⚠️' : 'ℹ️';
|
|
223
|
+
const where = finding.majors.length ? `**${majorsText(finding.majors)}:** ` : '';
|
|
224
|
+
lines.push(`- ${mark} ${where}${finding.text}`);
|
|
225
|
+
}
|
|
226
|
+
if (report.versions.some((v) => v.checks.partial.advisory)) {
|
|
227
|
+
lines.push('', '\\* Passed on `minVersion` alone, which has been too low before; the build decides.');
|
|
228
|
+
}
|
|
229
|
+
return lines.join('\n') + '\n';
|
|
230
|
+
}
|
|
231
|
+
export function renderJson(report) {
|
|
232
|
+
return JSON.stringify(report, null, 2) + '\n';
|
|
233
|
+
}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { freemem, availableParallelism } from 'node:os';
|
|
4
|
+
import { join, relative, sep } from 'node:path';
|
|
5
|
+
import { configureBuild, generateApp, readJson, writeJson } from "./app.js";
|
|
6
|
+
import { ngBuild } from "./build.js";
|
|
7
|
+
import { installLibrary } from "./install.js";
|
|
8
|
+
import { CHECKS, result } from "./model.js";
|
|
9
|
+
import { judgePartial } from "./partial.js";
|
|
10
|
+
import { planPeers } from "./peers.js";
|
|
11
|
+
import { entryPoints, hasSignalWriteBrand, readPublicApi, renderSmoke, writeSmoke } from "./smoke.js";
|
|
12
|
+
import { runLibraryTests, swapVersions } from "./tests.js";
|
|
13
|
+
/** About what one Angular production build needs; versions beyond what memory allows wait their turn. */
|
|
14
|
+
const MEMORY_PER_VERSION = 2.5 * 1024 ** 3;
|
|
15
|
+
export function defaultConcurrency(versions) {
|
|
16
|
+
const byMemory = Math.floor(freemem() / MEMORY_PER_VERSION);
|
|
17
|
+
return Math.max(1, Math.min(versions, byMemory, availableParallelism()));
|
|
18
|
+
}
|
|
19
|
+
export async function runAll(ctx, majors, concurrency) {
|
|
20
|
+
const reports = new Array(majors.length);
|
|
21
|
+
let next = 0;
|
|
22
|
+
const worker = async () => {
|
|
23
|
+
while (next < majors.length) {
|
|
24
|
+
const index = next++;
|
|
25
|
+
reports[index] = await checkVersion(ctx, majors[index]);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, majors.length) }, worker));
|
|
29
|
+
return reports;
|
|
30
|
+
}
|
|
31
|
+
function installedAngular(appDir, fallback) {
|
|
32
|
+
try {
|
|
33
|
+
const require = createRequire(join(appDir, 'package.json'));
|
|
34
|
+
return JSON.parse(readFileSync(require.resolve('@angular/core/package.json'), 'utf8')).version;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function checkVersion(ctx, angular) {
|
|
41
|
+
const started = Date.now();
|
|
42
|
+
const tag = `Angular ${angular.major}`;
|
|
43
|
+
const checks = Object.fromEntries(CHECKS.map((c) => [c, result('not-run')]));
|
|
44
|
+
if (!ctx.tests)
|
|
45
|
+
checks.tests = result('off', 'Not requested (--tests).');
|
|
46
|
+
const report = {
|
|
47
|
+
major: angular.major,
|
|
48
|
+
angularVersion: angular.version,
|
|
49
|
+
supported: angular.supported,
|
|
50
|
+
prerelease: angular.prerelease,
|
|
51
|
+
claimed: null,
|
|
52
|
+
peerMisses: [],
|
|
53
|
+
checks,
|
|
54
|
+
durationMs: 0,
|
|
55
|
+
};
|
|
56
|
+
const finish = () => {
|
|
57
|
+
report.durationMs = Date.now() - started;
|
|
58
|
+
ctx.log(`${tag}: done in ${Math.round(report.durationMs / 1000)} s`);
|
|
59
|
+
return report;
|
|
60
|
+
};
|
|
61
|
+
// Peer range: reported, but never stops the other checks.
|
|
62
|
+
const plan = await planPeers(ctx.library.manifest, angular.version, ctx.registry);
|
|
63
|
+
report.claimed = plan.claimed;
|
|
64
|
+
report.peerMisses = plan.misses;
|
|
65
|
+
checks.peer =
|
|
66
|
+
plan.claimed === null
|
|
67
|
+
? result('none', 'The package declares no @angular/* peer dependency.')
|
|
68
|
+
: plan.claimed
|
|
69
|
+
? result('pass', `Claimed: ${Object.entries(plan.checked).map(([n, v]) => `${n}@${v}`).join(', ')}.`)
|
|
70
|
+
: result('fail', plan.misses.map((m) => `${m.name} "${m.range}" doesn't allow ${m.version}`).join('; ') + '.');
|
|
71
|
+
const versionDir = join(ctx.workDir, `angular-${angular.major}`);
|
|
72
|
+
mkdirSync(versionDir, { recursive: true });
|
|
73
|
+
let appDir;
|
|
74
|
+
try {
|
|
75
|
+
ctx.log(`${tag}: creating the app (${ctx.consumer ? 'your --consumer app' : `ng new, CLI ${angular.cliVersion}`})`);
|
|
76
|
+
appDir = ctx.consumer
|
|
77
|
+
? await copyConsumer(ctx, angular, plan.installs, versionDir)
|
|
78
|
+
: await generateApp(angular, ctx.pm, versionDir, ctx.cacheDir, ctx.offline);
|
|
79
|
+
if (ctx.keep)
|
|
80
|
+
report.appDir = appDir;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
report.setupError = error.message;
|
|
84
|
+
if (!ctx.keep)
|
|
85
|
+
rmSync(versionDir, { recursive: true, force: true, maxRetries: 3 });
|
|
86
|
+
return finish();
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
ctx.log(`${tag}: installing ${ctx.library.manifest.name} with ${ctx.pm}`);
|
|
90
|
+
const installStarted = Date.now();
|
|
91
|
+
const installed = await installLibrary({
|
|
92
|
+
appDir,
|
|
93
|
+
pm: ctx.pm,
|
|
94
|
+
libraryName: ctx.library.manifest.name,
|
|
95
|
+
tarball: ctx.library.tarball,
|
|
96
|
+
plan,
|
|
97
|
+
angularMajor: angular.major,
|
|
98
|
+
cacheDir: ctx.cacheDir,
|
|
99
|
+
offline: ctx.offline,
|
|
100
|
+
});
|
|
101
|
+
checks.install = result(installed.pass ? 'pass' : 'fail', installed.message, {
|
|
102
|
+
errorLines: installed.errorLines,
|
|
103
|
+
durationMs: Date.now() - installStarted,
|
|
104
|
+
});
|
|
105
|
+
if (!installed.pass)
|
|
106
|
+
return finish();
|
|
107
|
+
const actual = installedAngular(appDir, angular.version);
|
|
108
|
+
report.angularVersion = actual;
|
|
109
|
+
const partial = judgePartial(ctx.summary, actual);
|
|
110
|
+
checks.partial = result(partial.pass ? 'pass' : 'fail', partial.message, {
|
|
111
|
+
advisory: partial.advisory,
|
|
112
|
+
hint: partial.pass ? undefined : 'Build the library with the oldest Angular you support, or take this version out of the peer range.',
|
|
113
|
+
});
|
|
114
|
+
if (!partial.pass)
|
|
115
|
+
return finish();
|
|
116
|
+
let files;
|
|
117
|
+
if (!ctx.consumer) {
|
|
118
|
+
try {
|
|
119
|
+
const api = readPublicApi(appDir, entryPoints(ctx.library.manifest));
|
|
120
|
+
writeSmoke(appDir, renderSmoke(api, { signalWriteBrand: hasSignalWriteBrand(appDir) }));
|
|
121
|
+
files = ['src/ngm-main.ts'];
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
checks.aot = result('fail', `Couldn't generate the smoke app from the package's typings: ${error.message}`, {
|
|
125
|
+
hint: 'If the typings are fine, give ng-matrix an example app with --consumer.',
|
|
126
|
+
});
|
|
127
|
+
return finish();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const project = configureBuild(appDir, files?.[0]);
|
|
131
|
+
ctx.log(`${tag}: AOT build`);
|
|
132
|
+
const aot = await ngBuild(appDir, project.tsConfig, { strictTemplates: false, skipLibCheck: true }, files);
|
|
133
|
+
checks.aot = result(aot.pass ? 'pass' : 'fail', aot.pass ? 'Production build succeeded.' : 'Production build failed.', {
|
|
134
|
+
errorLines: aot.errorLines,
|
|
135
|
+
hint: aot.hint,
|
|
136
|
+
durationMs: aot.durationMs,
|
|
137
|
+
});
|
|
138
|
+
if (!aot.pass)
|
|
139
|
+
return finish();
|
|
140
|
+
ctx.log(`${tag}: strict build`);
|
|
141
|
+
const strict = await ngBuild(appDir, project.tsConfig, { strictTemplates: true, skipLibCheck: false }, files);
|
|
142
|
+
checks.strict = result(strict.pass ? 'pass' : 'fail', strict.pass ? 'Builds with strictTemplates: true and skipLibCheck: false.' : 'Fails with strictTemplates: true and skipLibCheck: false.', { errorLines: strict.errorLines, hint: strict.hint, durationMs: strict.durationMs });
|
|
143
|
+
if (!strict.pass)
|
|
144
|
+
return finish();
|
|
145
|
+
if (ctx.tests) {
|
|
146
|
+
if (!ctx.workspace) {
|
|
147
|
+
checks.tests = result('off', 'No workspace to test: the package came from --tarball or --package.');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
ctx.log(`${tag}: library tests`);
|
|
151
|
+
const tests = await runLibraryTests({
|
|
152
|
+
workspace: ctx.workspace,
|
|
153
|
+
into: join(versionDir, 'tests'),
|
|
154
|
+
angular,
|
|
155
|
+
peerVersions: plan.installs,
|
|
156
|
+
pm: ctx.pm,
|
|
157
|
+
command: ctx.testCommand,
|
|
158
|
+
registry: ctx.registry,
|
|
159
|
+
cacheDir: ctx.cacheDir,
|
|
160
|
+
offline: ctx.offline,
|
|
161
|
+
});
|
|
162
|
+
checks.tests = result(tests.pass ? 'pass' : 'fail', tests.message, {
|
|
163
|
+
errorLines: tests.errorLines,
|
|
164
|
+
durationMs: tests.durationMs,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return finish();
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
if (!ctx.keep)
|
|
172
|
+
rmSync(versionDir, { recursive: true, force: true, maxRetries: 3 });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Copies the author's example app and moves its Angular dependencies to this version. */
|
|
176
|
+
async function copyConsumer(ctx, angular, peerVersions, versionDir) {
|
|
177
|
+
const source = ctx.consumer;
|
|
178
|
+
if (!existsSync(join(source, 'angular.json')))
|
|
179
|
+
throw new Error(`--consumer ${source} has no angular.json.`);
|
|
180
|
+
const appDir = join(versionDir, 'app');
|
|
181
|
+
const skip = new Set(['node_modules', '.angular', 'dist', '.git']);
|
|
182
|
+
cpSync(source, appDir, {
|
|
183
|
+
recursive: true,
|
|
184
|
+
filter: (path) => !relative(source, path).split(sep).some((part) => skip.has(part)),
|
|
185
|
+
});
|
|
186
|
+
for (const lock of ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock'])
|
|
187
|
+
rmSync(join(appDir, lock), { force: true });
|
|
188
|
+
const manifestPath = join(appDir, 'package.json');
|
|
189
|
+
const manifest = readJson(manifestPath);
|
|
190
|
+
const request = { angular, peerVersions };
|
|
191
|
+
if (manifest.dependencies)
|
|
192
|
+
manifest.dependencies = swapVersions(manifest.dependencies, request);
|
|
193
|
+
if (manifest.devDependencies)
|
|
194
|
+
manifest.devDependencies = swapVersions(manifest.devDependencies, request);
|
|
195
|
+
// The package under test comes from the tarball, not from wherever the example points.
|
|
196
|
+
delete manifest.dependencies?.[ctx.library.manifest.name];
|
|
197
|
+
delete manifest.devDependencies?.[ctx.library.manifest.name];
|
|
198
|
+
writeJson(manifestPath, manifest);
|
|
199
|
+
return appDir;
|
|
200
|
+
}
|