@titannio/webtoolkit-cli 1.3.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 +639 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +268 -0
- package/dist/bundle-audit.d.ts +7 -0
- package/dist/bundle-audit.js +111 -0
- package/dist/cleaner.d.ts +15 -0
- package/dist/cleaner.js +359 -0
- package/dist/config-reference.d.ts +8 -0
- package/dist/config-reference.js +805 -0
- package/dist/config.d.ts +306 -0
- package/dist/config.js +173 -0
- package/dist/dev-grid.d.ts +7 -0
- package/dist/dev-grid.js +181 -0
- package/dist/dev-watch.d.ts +13 -0
- package/dist/dev-watch.js +184 -0
- package/dist/environment.d.ts +10 -0
- package/dist/environment.js +172 -0
- package/dist/guard-registry.d.ts +1 -0
- package/dist/guard-registry.js +17 -0
- package/dist/guard-runner.d.ts +4 -0
- package/dist/guard-runner.js +36 -0
- package/dist/guards/any-guard.d.ts +16 -0
- package/dist/guards/any-guard.js +121 -0
- package/dist/guards/assert-no-tests-in-dist.d.ts +1 -0
- package/dist/guards/assert-no-tests-in-dist.js +56 -0
- package/dist/guards/check-mojibake.d.ts +52 -0
- package/dist/guards/check-mojibake.js +378 -0
- package/dist/guards/code-pattern-guard.d.ts +71 -0
- package/dist/guards/code-pattern-guard.js +654 -0
- package/dist/guards/dal-service-repository-check.d.ts +13 -0
- package/dist/guards/dal-service-repository-check.js +264 -0
- package/dist/guards/dependency-cruiser-guard.d.ts +14 -0
- package/dist/guards/dependency-cruiser-guard.js +69 -0
- package/dist/guards/documentation-guard.d.ts +3 -0
- package/dist/guards/documentation-guard.js +370 -0
- package/dist/guards/guard-config.d.ts +19 -0
- package/dist/guards/guard-config.js +87 -0
- package/dist/guards/internal-link-guard.d.ts +12 -0
- package/dist/guards/internal-link-guard.js +272 -0
- package/dist/guards/package-surface-guard.d.ts +37 -0
- package/dist/guards/package-surface-guard.js +234 -0
- package/dist/guards/pnpm-workspace-config.d.ts +2 -0
- package/dist/guards/pnpm-workspace-config.js +40 -0
- package/dist/guards/rebuild-preflight.d.ts +29 -0
- package/dist/guards/rebuild-preflight.js +137 -0
- package/dist/guards/repository-hygiene-guard.d.ts +12 -0
- package/dist/guards/repository-hygiene-guard.js +70 -0
- package/dist/guards/schema-guard.d.ts +10 -0
- package/dist/guards/schema-guard.js +160 -0
- package/dist/guards/singleton-deps-guard.d.ts +21 -0
- package/dist/guards/singleton-deps-guard.js +183 -0
- package/dist/guards/tsconfig-guard.d.ts +5 -0
- package/dist/guards/tsconfig-guard.js +105 -0
- package/dist/guards/workspace-manifest-guard.d.ts +21 -0
- package/dist/guards/workspace-manifest-guard.js +210 -0
- package/dist/jsdoc-report.d.ts +7 -0
- package/dist/jsdoc-report.js +456 -0
- package/dist/process.d.ts +20 -0
- package/dist/process.js +86 -0
- package/dist/ready-service.d.ts +7 -0
- package/dist/ready-service.js +135 -0
- package/dist/release-gate.d.ts +7 -0
- package/dist/release-gate.js +35 -0
- package/dist/repo-check.d.ts +7 -0
- package/dist/repo-check.js +121 -0
- package/dist/tasks.d.ts +17 -0
- package/dist/tasks.js +195 -0
- package/dist/upgrade.d.ts +10 -0
- package/dist/upgrade.js +674 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +51 -0
- package/dist/workspace-tests.d.ts +33 -0
- package/dist/workspace-tests.js +529 -0
- package/package.json +57 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
import { isMainModule, loadGuardConfig, resolveProjectPath } from './guard-config.js';
|
|
5
|
+
const colors = {
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
bright: '\x1b[1m',
|
|
8
|
+
green: '\x1b[32m',
|
|
9
|
+
};
|
|
10
|
+
function formatDiagnostics(relativePath, diagnostics) {
|
|
11
|
+
return diagnostics.map((diagnostic) => (`${relativePath}: TS${diagnostic.code}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`)).join('\n');
|
|
12
|
+
}
|
|
13
|
+
function readConfig(rootDir, relativePath) {
|
|
14
|
+
const absolutePath = resolveProjectPath(rootDir, relativePath);
|
|
15
|
+
if (!fs.existsSync(absolutePath))
|
|
16
|
+
throw new Error(`Config file not found: ${relativePath}`);
|
|
17
|
+
const result = ts.readConfigFile(absolutePath, ts.sys.readFile);
|
|
18
|
+
if (result.error) {
|
|
19
|
+
throw new Error(formatDiagnostics(relativePath, [result.error]));
|
|
20
|
+
}
|
|
21
|
+
const parsed = ts.parseJsonConfigFileContent(result.config, ts.sys, path.dirname(absolutePath), undefined, absolutePath);
|
|
22
|
+
if (parsed.errors.length > 0) {
|
|
23
|
+
throw new Error(formatDiagnostics(relativePath, parsed.errors));
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
raw: result.config,
|
|
27
|
+
parsed,
|
|
28
|
+
absolutePath,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function pointsToSourceInternals(values) {
|
|
32
|
+
return values.some((value) => /(^|[/\\])src([/\\]|$)/u.test(value));
|
|
33
|
+
}
|
|
34
|
+
export async function runTsconfigGuard(options = {}) {
|
|
35
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
36
|
+
const config = options.config ?? await loadGuardConfig('tsconfig', rootDir);
|
|
37
|
+
/* v8 ignore next -- both outcomes are asserted; V8 omits the fallthrough branch */
|
|
38
|
+
if (config.configs.length === 0) {
|
|
39
|
+
throw new Error('guards.tsconfig.configs must not be empty.');
|
|
40
|
+
}
|
|
41
|
+
const errors = [];
|
|
42
|
+
for (const check of config.configs) {
|
|
43
|
+
const loaded = readConfig(rootDir, check.path);
|
|
44
|
+
const rawInclude = Array.isArray(loaded.raw.include) ? loaded.raw.include : [];
|
|
45
|
+
const aliases = loaded.parsed.options.paths ?? {};
|
|
46
|
+
for (const requiredInclude of check.requiredIncludes ?? []) {
|
|
47
|
+
if (!rawInclude.includes(requiredInclude)) {
|
|
48
|
+
errors.push(`${check.path}: include must contain "${requiredInclude}".`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const expectedOptions = check.compilerOptions ?? {};
|
|
52
|
+
const converted = ts.convertCompilerOptionsFromJson(expectedOptions, path.dirname(loaded.absolutePath), check.path);
|
|
53
|
+
if (converted.errors.length > 0) {
|
|
54
|
+
throw new Error(formatDiagnostics(check.path, converted.errors));
|
|
55
|
+
}
|
|
56
|
+
for (const [option, expected] of Object.entries(expectedOptions)) {
|
|
57
|
+
const actual = loaded.parsed.options[option];
|
|
58
|
+
const normalizedExpected = converted.options[option];
|
|
59
|
+
if (actual !== normalizedExpected) {
|
|
60
|
+
errors.push(`${check.path}: compilerOptions.${option} must equal ${JSON.stringify(expected)}; found ${JSON.stringify(actual)}.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (config.packageScope) {
|
|
64
|
+
for (const alias of Object.keys(aliases)) {
|
|
65
|
+
if (alias.startsWith(config.packageScope) && alias !== config.packageScope && !alias.startsWith(`${config.packageScope}/`)) {
|
|
66
|
+
errors.push(`${check.path}: alias "${alias}" must use the "${config.packageScope}/package-name" convention.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const alias of check.publicAliases ?? []) {
|
|
71
|
+
const targets = aliases[alias] ?? [];
|
|
72
|
+
if (pointsToSourceInternals(targets)) {
|
|
73
|
+
errors.push(`${check.path}: public alias "${alias}" cannot point to package src internals.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const check of config.textFiles ?? []) {
|
|
78
|
+
const absolutePath = resolveProjectPath(rootDir, check.path);
|
|
79
|
+
if (!fs.existsSync(absolutePath))
|
|
80
|
+
throw new Error(`Text file not found: ${check.path}`);
|
|
81
|
+
const content = fs.readFileSync(absolutePath, 'utf8');
|
|
82
|
+
for (const forbidden of check.forbiddenStrings) {
|
|
83
|
+
if (content.includes(forbidden))
|
|
84
|
+
errors.push(`${check.path}: contains forbidden text "${forbidden}".`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
console.error('\nTSConfig guard failed:');
|
|
89
|
+
for (const error of errors)
|
|
90
|
+
console.error(` - ${error}`);
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
console.info(`✅${colors.green}${colors.bright} [OK]${colors.reset} TSConfig guard in compliance`);
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
/* v8 ignore start -- executable adapter */
|
|
97
|
+
if (isMainModule(import.meta.url)) {
|
|
98
|
+
runTsconfigGuard().then((code) => {
|
|
99
|
+
process.exitCode = code;
|
|
100
|
+
}).catch((error) => {
|
|
101
|
+
console.error(error.message);
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/* v8 ignore stop */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WorkspaceManifestGuardConfig } from '../config.js';
|
|
2
|
+
import { type ManifestDependencies } from './singleton-deps-guard.js';
|
|
3
|
+
type WorkspaceManifest = ManifestDependencies & {
|
|
4
|
+
name?: unknown;
|
|
5
|
+
};
|
|
6
|
+
type LoadedManifest = {
|
|
7
|
+
absoluteDirectory: string;
|
|
8
|
+
filePath: string;
|
|
9
|
+
manifest: WorkspaceManifest;
|
|
10
|
+
};
|
|
11
|
+
export type WorkspaceManifestIssue = {
|
|
12
|
+
filePath: string;
|
|
13
|
+
dependency?: string;
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function inspectWorkspaceManifests(rootDir: string, manifests: LoadedManifest[], config: WorkspaceManifestGuardConfig): WorkspaceManifestIssue[];
|
|
17
|
+
export declare function runWorkspaceManifestGuard(options?: {
|
|
18
|
+
rootDir?: string;
|
|
19
|
+
config?: WorkspaceManifestGuardConfig;
|
|
20
|
+
}): Promise<number>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { assertConfiguredScanScope, isMainModule, loadGuardConfig, resolveProjectPath, } from './guard-config.js';
|
|
5
|
+
import { MANIFEST_FIELDS, } from './singleton-deps-guard.js';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const semver = require('semver');
|
|
8
|
+
const runtimeFields = ['dependencies', 'peerDependencies', 'optionalDependencies'];
|
|
9
|
+
function normalizedRelativePath(rootDir, filePath) {
|
|
10
|
+
return path.relative(rootDir, filePath).replaceAll('\\', '/');
|
|
11
|
+
}
|
|
12
|
+
function pathKey(value) {
|
|
13
|
+
const normalized = path.resolve(value);
|
|
14
|
+
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
15
|
+
}
|
|
16
|
+
function readManifest(rootDir, manifestPath) {
|
|
17
|
+
let parsed;
|
|
18
|
+
try {
|
|
19
|
+
parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw new Error(`${normalizedRelativePath(rootDir, manifestPath)}: invalid package.json: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
25
|
+
throw new Error(`${normalizedRelativePath(rootDir, manifestPath)}: package.json must contain an object.`);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
absoluteDirectory: path.dirname(manifestPath),
|
|
29
|
+
filePath: normalizedRelativePath(rootDir, manifestPath),
|
|
30
|
+
manifest: parsed,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function collectManifests(rootDir, packageRoots) {
|
|
34
|
+
const manifestPaths = [];
|
|
35
|
+
for (const configuredRoot of packageRoots) {
|
|
36
|
+
const absoluteRoot = resolveProjectPath(rootDir, configuredRoot);
|
|
37
|
+
if (!fs.existsSync(absoluteRoot) || !fs.statSync(absoluteRoot).isDirectory())
|
|
38
|
+
continue;
|
|
39
|
+
for (const entry of fs.readdirSync(absoluteRoot, { withFileTypes: true })) {
|
|
40
|
+
if (!entry.isDirectory())
|
|
41
|
+
continue;
|
|
42
|
+
const manifestPath = path.join(absoluteRoot, entry.name, 'package.json');
|
|
43
|
+
if (fs.existsSync(manifestPath) && fs.statSync(manifestPath).isFile()) {
|
|
44
|
+
manifestPaths.push(manifestPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
assertConfiguredScanScope({
|
|
49
|
+
root: rootDir,
|
|
50
|
+
guardName: 'workspace-manifest',
|
|
51
|
+
configPath: 'guards.workspaceManifest.packageRoots',
|
|
52
|
+
configuredPaths: packageRoots,
|
|
53
|
+
eligibleFiles: manifestPaths,
|
|
54
|
+
});
|
|
55
|
+
return manifestPaths.sort().map((manifestPath) => readManifest(rootDir, manifestPath));
|
|
56
|
+
}
|
|
57
|
+
function isValidWorkspaceRange(value) {
|
|
58
|
+
const range = value.slice('workspace:'.length);
|
|
59
|
+
return range === '*' || range === '^' || range === '~' || semver.validRange(range) !== null;
|
|
60
|
+
}
|
|
61
|
+
function hasNonSemverProtocol(value) {
|
|
62
|
+
return /^(?:file|link|npm|git(?:\+\w+)?|https?|ssh|github|gitlab|bitbucket):/u.test(value);
|
|
63
|
+
}
|
|
64
|
+
function isValidVersionSpecifier(value, field) {
|
|
65
|
+
if (value.startsWith('workspace:'))
|
|
66
|
+
return isValidWorkspaceRange(value);
|
|
67
|
+
if (semver.validRange(value) !== null || hasNonSemverProtocol(value))
|
|
68
|
+
return true;
|
|
69
|
+
return field !== 'peerDependencies' && /^[A-Za-z][\w.-]*$/u.test(value);
|
|
70
|
+
}
|
|
71
|
+
function dependencyEntries(manifest) {
|
|
72
|
+
return MANIFEST_FIELDS.flatMap((field) => (Object.entries(manifest[field] ?? {}).map(([dependency, version]) => ({
|
|
73
|
+
field,
|
|
74
|
+
dependency,
|
|
75
|
+
version,
|
|
76
|
+
}))));
|
|
77
|
+
}
|
|
78
|
+
function requirementPackage(rootDir, manifestByDirectory, configuredPath, configPath) {
|
|
79
|
+
const absoluteDirectory = resolveProjectPath(rootDir, configuredPath);
|
|
80
|
+
const loaded = manifestByDirectory.get(pathKey(absoluteDirectory));
|
|
81
|
+
if (!loaded) {
|
|
82
|
+
throw new Error(`workspace-manifest: ${configPath} must reference a discovered workspace package containing package.json: ${configuredPath}`);
|
|
83
|
+
}
|
|
84
|
+
return loaded;
|
|
85
|
+
}
|
|
86
|
+
function checkPeerRequirement(rootDir, requirement, index, manifestByDirectory) {
|
|
87
|
+
const issues = [];
|
|
88
|
+
for (const [providerIndex, providerPath] of requirement.providers.entries()) {
|
|
89
|
+
const provider = requirementPackage(rootDir, manifestByDirectory, providerPath, `guards.workspaceManifest.peerRequirements[${index}].providers[${providerIndex}]`);
|
|
90
|
+
if (!provider.manifest.peerDependencies?.[requirement.dependency]) {
|
|
91
|
+
issues.push({
|
|
92
|
+
filePath: provider.filePath,
|
|
93
|
+
dependency: requirement.dependency,
|
|
94
|
+
message: 'must be declared in peerDependencies for this provider',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (provider.manifest.dependencies?.[requirement.dependency] ||
|
|
98
|
+
provider.manifest.optionalDependencies?.[requirement.dependency]) {
|
|
99
|
+
issues.push({
|
|
100
|
+
filePath: provider.filePath,
|
|
101
|
+
dependency: requirement.dependency,
|
|
102
|
+
message: 'must not be declared in provider runtime dependencies',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const [consumerIndex, consumerPath] of requirement.consumers.entries()) {
|
|
107
|
+
const consumer = requirementPackage(rootDir, manifestByDirectory, consumerPath, `guards.workspaceManifest.peerRequirements[${index}].consumers[${consumerIndex}]`);
|
|
108
|
+
const directRuntimeDeclaration = runtimeFields.some((field) => (Boolean(consumer.manifest[field]?.[requirement.dependency])));
|
|
109
|
+
if (!directRuntimeDeclaration) {
|
|
110
|
+
issues.push({
|
|
111
|
+
filePath: consumer.filePath,
|
|
112
|
+
dependency: requirement.dependency,
|
|
113
|
+
message: 'must be declared directly in a runtime dependency section for this consumer',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return issues;
|
|
118
|
+
}
|
|
119
|
+
export function inspectWorkspaceManifests(rootDir, manifests, config) {
|
|
120
|
+
const issues = [];
|
|
121
|
+
const manifestsByName = new Map();
|
|
122
|
+
const duplicateNames = new Set();
|
|
123
|
+
for (const loaded of manifests) {
|
|
124
|
+
if (typeof loaded.manifest.name !== 'string' || loaded.manifest.name.trim() === '') {
|
|
125
|
+
issues.push({ filePath: loaded.filePath, message: 'workspace package name is required' });
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const existing = manifestsByName.get(loaded.manifest.name);
|
|
129
|
+
if (existing) {
|
|
130
|
+
duplicateNames.add(loaded.manifest.name);
|
|
131
|
+
issues.push({
|
|
132
|
+
filePath: loaded.filePath,
|
|
133
|
+
dependency: loaded.manifest.name,
|
|
134
|
+
message: `duplicates workspace package name from ${existing.filePath}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
manifestsByName.set(loaded.manifest.name, loaded);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const internalNames = new Set([...manifestsByName.keys()].filter((name) => !duplicateNames.has(name)));
|
|
142
|
+
for (const loaded of manifests) {
|
|
143
|
+
for (const { field, dependency, version } of dependencyEntries(loaded.manifest)) {
|
|
144
|
+
if (!isValidVersionSpecifier(version, field)) {
|
|
145
|
+
issues.push({
|
|
146
|
+
filePath: loaded.filePath,
|
|
147
|
+
dependency,
|
|
148
|
+
message: `has invalid version range ${JSON.stringify(version)} in ${field}`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (config.requireWorkspaceProtocol &&
|
|
152
|
+
internalNames.has(dependency) &&
|
|
153
|
+
!version.startsWith('workspace:')) {
|
|
154
|
+
issues.push({
|
|
155
|
+
filePath: loaded.filePath,
|
|
156
|
+
dependency,
|
|
157
|
+
message: `must use the workspace: protocol in ${field}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const dependencies = new Set(runtimeFields.flatMap((field) => (Object.keys(loaded.manifest[field] ?? {}))));
|
|
162
|
+
for (const dependency of dependencies) {
|
|
163
|
+
const declaredFields = runtimeFields.filter((field) => (Boolean(loaded.manifest[field]?.[dependency])));
|
|
164
|
+
if (declaredFields.length > 1) {
|
|
165
|
+
issues.push({
|
|
166
|
+
filePath: loaded.filePath,
|
|
167
|
+
dependency,
|
|
168
|
+
message: `is declared in conflicting runtime sections: ${declaredFields.join(', ')}`,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const manifestByDirectory = new Map(manifests.map((loaded) => [pathKey(loaded.absoluteDirectory), loaded]));
|
|
174
|
+
for (const [index, requirement] of config.peerRequirements.entries()) {
|
|
175
|
+
issues.push(...checkPeerRequirement(rootDir, requirement, index, manifestByDirectory));
|
|
176
|
+
}
|
|
177
|
+
return issues.sort((left, right) => (left.filePath.localeCompare(right.filePath) ||
|
|
178
|
+
(left.dependency ?? '').localeCompare(right.dependency ?? '') ||
|
|
179
|
+
left.message.localeCompare(right.message)));
|
|
180
|
+
}
|
|
181
|
+
export async function runWorkspaceManifestGuard(options = {}) {
|
|
182
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
183
|
+
const config = options.config ?? await loadGuardConfig('workspaceManifest', rootDir);
|
|
184
|
+
/* v8 ignore next -- both outcomes are asserted; V8 omits the fallthrough branch */
|
|
185
|
+
if (config.packageRoots.length === 0) {
|
|
186
|
+
throw new Error('guards.workspaceManifest.packageRoots must not be empty.');
|
|
187
|
+
}
|
|
188
|
+
const manifests = collectManifests(rootDir, config.packageRoots);
|
|
189
|
+
const issues = inspectWorkspaceManifests(rootDir, manifests, config);
|
|
190
|
+
if (issues.length === 0) {
|
|
191
|
+
console.info(`Workspace manifests are valid (${manifests.length} packages).`);
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
console.error('Workspace manifest guard failed:');
|
|
195
|
+
for (const issue of issues) {
|
|
196
|
+
const dependency = issue.dependency ? ` [${issue.dependency}]` : '';
|
|
197
|
+
console.error(` - ${issue.filePath}${dependency}: ${issue.message}`);
|
|
198
|
+
}
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
/* v8 ignore start -- executable adapter */
|
|
202
|
+
if (isMainModule(import.meta.url)) {
|
|
203
|
+
runWorkspaceManifestGuard().then((code) => {
|
|
204
|
+
process.exitCode = code;
|
|
205
|
+
}).catch((error) => {
|
|
206
|
+
console.error(error.message);
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/* v8 ignore stop */
|