@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
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
export type CleanLevel = 'empty' | 'cache' | 'deep' | 'nuclear';
|
|
2
|
+
export type ReinstallPolicy = 'ask' | 'always' | 'never';
|
|
3
|
+
export type RemovalKind = 'dir' | 'file' | 'empty-dir';
|
|
4
|
+
export type CleanerOptions = {
|
|
5
|
+
level?: CleanLevel;
|
|
6
|
+
dryRun: boolean;
|
|
7
|
+
noStorePrune: boolean;
|
|
8
|
+
interactive: boolean;
|
|
9
|
+
reinstall: ReinstallPolicy;
|
|
10
|
+
};
|
|
11
|
+
export type Removal = {
|
|
12
|
+
kind: RemovalKind;
|
|
13
|
+
relPath: string;
|
|
14
|
+
};
|
|
15
|
+
export type LevelConfig = {
|
|
16
|
+
label: string;
|
|
17
|
+
removeEmptyDirs: boolean;
|
|
18
|
+
removableDirNames: string[];
|
|
19
|
+
removableFileNames: string[];
|
|
20
|
+
removableFileSuffixes: string[];
|
|
21
|
+
removableFilePrefixes: string[];
|
|
22
|
+
removableFilePatterns: string[];
|
|
23
|
+
removableSpecificFiles: string[];
|
|
24
|
+
};
|
|
25
|
+
export type CleanerConfig = {
|
|
26
|
+
workspaceRootNames: string[];
|
|
27
|
+
protectedRootNames: string[];
|
|
28
|
+
skipEmptyDirNames: string[];
|
|
29
|
+
skipArtifactDirNames: string[];
|
|
30
|
+
levels: Record<CleanLevel, LevelConfig>;
|
|
31
|
+
};
|
|
32
|
+
export type WebToolkitCliConfig = {
|
|
33
|
+
packageManager: string;
|
|
34
|
+
cleaner: CleanerConfig;
|
|
35
|
+
tasks: Record<string, TaskConfig>;
|
|
36
|
+
guards?: GuardsConfig;
|
|
37
|
+
documentation?: DocumentationConfig;
|
|
38
|
+
workspaceTests?: WorkspaceTestsConfig;
|
|
39
|
+
repoCheck?: RepoCheckConfig;
|
|
40
|
+
releaseGate?: ReleaseGateConfig;
|
|
41
|
+
validate?: ValidateConfig;
|
|
42
|
+
jsdocReport?: JSDocReportConfig;
|
|
43
|
+
bundleAudit?: BundleAuditConfig;
|
|
44
|
+
upgrade?: UpgradeConfig;
|
|
45
|
+
devWatch?: DevWatchConfig;
|
|
46
|
+
devGrid?: DevGridConfig;
|
|
47
|
+
environment?: EnvironmentConfig;
|
|
48
|
+
};
|
|
49
|
+
export type PathScanGuardConfig = {
|
|
50
|
+
includePaths: string[];
|
|
51
|
+
excludePatterns?: string[];
|
|
52
|
+
};
|
|
53
|
+
export type SchemaGuardConfig = PathScanGuardConfig & {
|
|
54
|
+
centralDirectory: string;
|
|
55
|
+
builders: string[];
|
|
56
|
+
};
|
|
57
|
+
export type AnyGuardConfig = PathScanGuardConfig;
|
|
58
|
+
export type RebuildPreflightTargetConfig = {
|
|
59
|
+
warningTitle: string;
|
|
60
|
+
turboFilters: string[];
|
|
61
|
+
relevantBuildPackages: string[];
|
|
62
|
+
};
|
|
63
|
+
export type RebuildPreflightGuardConfig = {
|
|
64
|
+
targets: Record<string, RebuildPreflightTargetConfig>;
|
|
65
|
+
};
|
|
66
|
+
export type TsconfigFileCheckConfig = {
|
|
67
|
+
path: string;
|
|
68
|
+
requiredIncludes?: string[];
|
|
69
|
+
compilerOptions?: Record<string, string | number | boolean>;
|
|
70
|
+
publicAliases?: string[];
|
|
71
|
+
};
|
|
72
|
+
export type TextFileCheckConfig = {
|
|
73
|
+
path: string;
|
|
74
|
+
forbiddenStrings: string[];
|
|
75
|
+
};
|
|
76
|
+
export type TsconfigGuardConfig = {
|
|
77
|
+
packageScope?: string;
|
|
78
|
+
configs: TsconfigFileCheckConfig[];
|
|
79
|
+
textFiles?: TextFileCheckConfig[];
|
|
80
|
+
};
|
|
81
|
+
export type LayerConfig = {
|
|
82
|
+
name: string;
|
|
83
|
+
paths: string[];
|
|
84
|
+
exclude?: string[];
|
|
85
|
+
};
|
|
86
|
+
export type DalServiceRepositoryGuardConfig = {
|
|
87
|
+
sourceDirectory: string;
|
|
88
|
+
tsconfig: string;
|
|
89
|
+
excludePatterns?: string[];
|
|
90
|
+
layers: LayerConfig[];
|
|
91
|
+
forbiddenDependencies: Record<string, string[]>;
|
|
92
|
+
};
|
|
93
|
+
export type CodePatternRuleConfig = {
|
|
94
|
+
includePaths: string[];
|
|
95
|
+
allowedPathPatterns?: string[];
|
|
96
|
+
};
|
|
97
|
+
export type CodePatternGuardConfig = {
|
|
98
|
+
tsconfig: string;
|
|
99
|
+
modelsDirectory: string;
|
|
100
|
+
rules: Record<string, CodePatternRuleConfig>;
|
|
101
|
+
};
|
|
102
|
+
export type WorkspacePeerRequirementConfig = {
|
|
103
|
+
dependency: string;
|
|
104
|
+
providers: string[];
|
|
105
|
+
consumers: string[];
|
|
106
|
+
};
|
|
107
|
+
export type WorkspaceManifestGuardConfig = {
|
|
108
|
+
packageRoots: string[];
|
|
109
|
+
requireWorkspaceProtocol: boolean;
|
|
110
|
+
peerRequirements: WorkspacePeerRequirementConfig[];
|
|
111
|
+
};
|
|
112
|
+
export type RepositoryHygieneGuardConfig = {
|
|
113
|
+
forbiddenPathPatterns: string[];
|
|
114
|
+
allowedPathPatterns: string[];
|
|
115
|
+
};
|
|
116
|
+
export type PackageSurfaceGuardConfig = {
|
|
117
|
+
packageDirectories: string[];
|
|
118
|
+
forbiddenPublishedPatterns: string[];
|
|
119
|
+
};
|
|
120
|
+
export type GuardsConfig = {
|
|
121
|
+
any?: AnyGuardConfig;
|
|
122
|
+
codePattern?: CodePatternGuardConfig;
|
|
123
|
+
dalServiceRepository?: DalServiceRepositoryGuardConfig;
|
|
124
|
+
internalLink?: PathScanGuardConfig;
|
|
125
|
+
packageSurface?: PackageSurfaceGuardConfig;
|
|
126
|
+
rebuildPreflight?: RebuildPreflightGuardConfig;
|
|
127
|
+
repositoryHygiene?: RepositoryHygieneGuardConfig;
|
|
128
|
+
schema?: SchemaGuardConfig;
|
|
129
|
+
tsconfig?: TsconfigGuardConfig;
|
|
130
|
+
workspaceManifest?: WorkspaceManifestGuardConfig;
|
|
131
|
+
};
|
|
132
|
+
export type DocumentationMetadataRule = {
|
|
133
|
+
equals?: string;
|
|
134
|
+
unique?: boolean;
|
|
135
|
+
repositoryPaths?: boolean;
|
|
136
|
+
minItems?: number;
|
|
137
|
+
};
|
|
138
|
+
export type DocumentationPairedDocumentsConfig = {
|
|
139
|
+
target: string;
|
|
140
|
+
index?: string;
|
|
141
|
+
table?: {
|
|
142
|
+
header: string[];
|
|
143
|
+
fileColumn: string;
|
|
144
|
+
minRows?: number;
|
|
145
|
+
};
|
|
146
|
+
finalSection?: {
|
|
147
|
+
heading: string;
|
|
148
|
+
minItems?: number;
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
export type DocumentationCollectionConfig = {
|
|
152
|
+
files: string[];
|
|
153
|
+
exclude?: string[];
|
|
154
|
+
index?: string;
|
|
155
|
+
metadata?: Record<string, DocumentationMetadataRule>;
|
|
156
|
+
pairedDocuments?: DocumentationPairedDocumentsConfig;
|
|
157
|
+
};
|
|
158
|
+
export type DocumentationConfig = {
|
|
159
|
+
files: string[];
|
|
160
|
+
excludeDirectories?: string[];
|
|
161
|
+
checks?: {
|
|
162
|
+
singleH1?: boolean;
|
|
163
|
+
headingOrder?: boolean;
|
|
164
|
+
localLinks?: boolean;
|
|
165
|
+
reachability?: {
|
|
166
|
+
entrypoints: string[];
|
|
167
|
+
files: string[];
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
requiredFiles?: string[];
|
|
171
|
+
collections?: DocumentationCollectionConfig[];
|
|
172
|
+
inventories?: Array<{
|
|
173
|
+
document: string;
|
|
174
|
+
sources: string[];
|
|
175
|
+
minMatches?: number;
|
|
176
|
+
}>;
|
|
177
|
+
};
|
|
178
|
+
export type TaskOutputMode = 'inherit' | 'buffered';
|
|
179
|
+
type TaskStepBaseConfig = {
|
|
180
|
+
label: string;
|
|
181
|
+
args?: string[];
|
|
182
|
+
cwd?: string;
|
|
183
|
+
env?: Record<string, string>;
|
|
184
|
+
appendArgs?: boolean;
|
|
185
|
+
outputMode?: TaskOutputMode;
|
|
186
|
+
};
|
|
187
|
+
export type CommandStepConfig = TaskStepBaseConfig & {
|
|
188
|
+
command: string;
|
|
189
|
+
builtinGuard?: never;
|
|
190
|
+
};
|
|
191
|
+
export type BuiltinGuardStepConfig = TaskStepBaseConfig & {
|
|
192
|
+
builtinGuard: string;
|
|
193
|
+
command?: never;
|
|
194
|
+
};
|
|
195
|
+
export type TaskStepConfig = CommandStepConfig | BuiltinGuardStepConfig;
|
|
196
|
+
export type TaskConfig = {
|
|
197
|
+
title?: string;
|
|
198
|
+
failFast?: boolean;
|
|
199
|
+
outputMode?: TaskOutputMode;
|
|
200
|
+
steps: TaskStepConfig[];
|
|
201
|
+
};
|
|
202
|
+
export type WorkspaceTargetConfig = {
|
|
203
|
+
name: string;
|
|
204
|
+
package: string;
|
|
205
|
+
path: string;
|
|
206
|
+
};
|
|
207
|
+
export type WorkspaceTestsConfig = {
|
|
208
|
+
workspaces: WorkspaceTargetConfig[];
|
|
209
|
+
errorLogFile?: string;
|
|
210
|
+
testFilePattern?: string;
|
|
211
|
+
ignoreDirNames?: string[];
|
|
212
|
+
maxFailureExcerptLines?: number;
|
|
213
|
+
};
|
|
214
|
+
export type RepoCheckConfig = {
|
|
215
|
+
title?: string;
|
|
216
|
+
failFast?: boolean;
|
|
217
|
+
steps: TaskStepConfig[];
|
|
218
|
+
};
|
|
219
|
+
export type ReleaseGateStageConfig = {
|
|
220
|
+
name: string;
|
|
221
|
+
command?: string;
|
|
222
|
+
args?: string[];
|
|
223
|
+
package?: string;
|
|
224
|
+
script?: string;
|
|
225
|
+
files?: string[];
|
|
226
|
+
};
|
|
227
|
+
export type ReleaseGateConfig = {
|
|
228
|
+
stages: ReleaseGateStageConfig[];
|
|
229
|
+
};
|
|
230
|
+
export type ValidateConfig = {
|
|
231
|
+
steps: TaskStepConfig[];
|
|
232
|
+
postSteps?: TaskStepConfig[];
|
|
233
|
+
};
|
|
234
|
+
export type JSDocReportConfig = {
|
|
235
|
+
includePaths: string[];
|
|
236
|
+
excludePatterns?: string[];
|
|
237
|
+
reportFile?: string;
|
|
238
|
+
maxLineLength?: number;
|
|
239
|
+
promptForReport?: boolean;
|
|
240
|
+
};
|
|
241
|
+
export type BundleAuditConfig = {
|
|
242
|
+
appDirs: string[];
|
|
243
|
+
top?: number;
|
|
244
|
+
rawWarningBytes?: number;
|
|
245
|
+
};
|
|
246
|
+
export type UpgradeConfig = {
|
|
247
|
+
defaultCooldownDays?: number;
|
|
248
|
+
protectedDependencyUpstreamHints?: Record<string, string[]>;
|
|
249
|
+
protectedOverridesFile?: string;
|
|
250
|
+
singletonGuardCommand?: CommandStepConfig;
|
|
251
|
+
};
|
|
252
|
+
export type DevAppConfig = {
|
|
253
|
+
displayName: string;
|
|
254
|
+
port: number;
|
|
255
|
+
filter?: string;
|
|
256
|
+
color?: string;
|
|
257
|
+
};
|
|
258
|
+
export type DevWatchConfig = {
|
|
259
|
+
apps: Record<string, DevAppConfig>;
|
|
260
|
+
defaultApps: string[];
|
|
261
|
+
backendApp?: string;
|
|
262
|
+
host?: string;
|
|
263
|
+
backendPortCleanupGraceMs?: number;
|
|
264
|
+
};
|
|
265
|
+
export type DevGridPaneConfig = {
|
|
266
|
+
title: string;
|
|
267
|
+
command: string;
|
|
268
|
+
silentCommand?: string;
|
|
269
|
+
fontSize?: number;
|
|
270
|
+
};
|
|
271
|
+
export type DevGridRowConfig = {
|
|
272
|
+
panes: DevGridPaneConfig[];
|
|
273
|
+
};
|
|
274
|
+
export type DevGridLayoutConfig = {
|
|
275
|
+
rows: DevGridRowConfig[];
|
|
276
|
+
};
|
|
277
|
+
export type DevGridConfig = {
|
|
278
|
+
layout: DevGridLayoutConfig;
|
|
279
|
+
fallbackScript?: string;
|
|
280
|
+
silentFallbackScript?: string;
|
|
281
|
+
preflightCommand?: CommandStepConfig;
|
|
282
|
+
};
|
|
283
|
+
export type EnvironmentConfig = {
|
|
284
|
+
requiredNodeMajor?: number;
|
|
285
|
+
packageManager?: string;
|
|
286
|
+
corepackHome?: string;
|
|
287
|
+
};
|
|
288
|
+
type PartialLevelConfig = Partial<Omit<LevelConfig, 'removableFilePatterns'> & {
|
|
289
|
+
removableFilePatterns: string[];
|
|
290
|
+
}>;
|
|
291
|
+
type PartialCleanerConfig = Partial<Omit<CleanerConfig, 'levels'> & {
|
|
292
|
+
levels: Partial<Record<CleanLevel, PartialLevelConfig>>;
|
|
293
|
+
}>;
|
|
294
|
+
type PartialWebToolkitCliConfig = Partial<Omit<WebToolkitCliConfig, 'cleaner'> & {
|
|
295
|
+
cleaner: PartialCleanerConfig;
|
|
296
|
+
tasks: Record<string, TaskConfig>;
|
|
297
|
+
}>;
|
|
298
|
+
export declare const defaultConfig: WebToolkitCliConfig;
|
|
299
|
+
export declare function pathExists(target: string): Promise<boolean>;
|
|
300
|
+
export declare function findConfigPath(startDir: string): Promise<string | null>;
|
|
301
|
+
export declare function mergeConfig(override?: PartialWebToolkitCliConfig): WebToolkitCliConfig;
|
|
302
|
+
export declare function loadConfig(cwd: string): Promise<{
|
|
303
|
+
config: WebToolkitCliConfig;
|
|
304
|
+
configPath: string | null;
|
|
305
|
+
}>;
|
|
306
|
+
export {};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { access, readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { validateConfig } from './config-reference.js';
|
|
4
|
+
const configFileName = 'config.json';
|
|
5
|
+
const configDirName = '.webtoolkit-cli';
|
|
6
|
+
const cacheDirNames = [
|
|
7
|
+
'.turbo',
|
|
8
|
+
'.cache',
|
|
9
|
+
'.parcel-cache',
|
|
10
|
+
'.vite',
|
|
11
|
+
'.next',
|
|
12
|
+
'.nuxt',
|
|
13
|
+
'.nyc_output',
|
|
14
|
+
'.rpt2_cache',
|
|
15
|
+
'.rts2_cache_cjs',
|
|
16
|
+
'.rts2_cache_es',
|
|
17
|
+
'.rts2_cache_umd',
|
|
18
|
+
'.wrangler',
|
|
19
|
+
];
|
|
20
|
+
const deepDirNames = ['dist', 'build', 'coverage'];
|
|
21
|
+
const cacheFileNames = ['.eslintcache', '.stylelintcache', 'vitest-results.json'];
|
|
22
|
+
const deepFileSuffixes = ['.tsbuildinfo', '.lcov'];
|
|
23
|
+
const deepFilePrefixes = ['vite.config.ts.timestamp-'];
|
|
24
|
+
const deepFilePatterns = [
|
|
25
|
+
'^npm-debug\\.log',
|
|
26
|
+
'^yarn-debug\\.log',
|
|
27
|
+
'^yarn-error\\.log',
|
|
28
|
+
'^lerna-debug\\.log',
|
|
29
|
+
'^\\.pnpm-debug\\.log',
|
|
30
|
+
'^report\\.\\d+\\.\\d+\\.\\d+\\.\\d+\\.json$',
|
|
31
|
+
];
|
|
32
|
+
export const defaultConfig = {
|
|
33
|
+
packageManager: 'pnpm',
|
|
34
|
+
tasks: {},
|
|
35
|
+
cleaner: {
|
|
36
|
+
workspaceRootNames: ['apps', 'packages'],
|
|
37
|
+
protectedRootNames: ['apps', 'scripts'],
|
|
38
|
+
skipEmptyDirNames: [
|
|
39
|
+
'node_modules',
|
|
40
|
+
'.pnpm-store',
|
|
41
|
+
'.git',
|
|
42
|
+
'.idea',
|
|
43
|
+
'.vscode',
|
|
44
|
+
'.vercel',
|
|
45
|
+
'.trae',
|
|
46
|
+
'.agent',
|
|
47
|
+
],
|
|
48
|
+
skipArtifactDirNames: ['.git'],
|
|
49
|
+
levels: {
|
|
50
|
+
empty: {
|
|
51
|
+
label: 'Empty directories only',
|
|
52
|
+
removeEmptyDirs: true,
|
|
53
|
+
removableDirNames: [],
|
|
54
|
+
removableFileNames: [],
|
|
55
|
+
removableFileSuffixes: [],
|
|
56
|
+
removableFilePrefixes: [],
|
|
57
|
+
removableFilePatterns: [],
|
|
58
|
+
removableSpecificFiles: [],
|
|
59
|
+
},
|
|
60
|
+
cache: {
|
|
61
|
+
label: 'Caches and temp artifacts',
|
|
62
|
+
removeEmptyDirs: true,
|
|
63
|
+
removableDirNames: cacheDirNames,
|
|
64
|
+
removableFileNames: cacheFileNames,
|
|
65
|
+
removableFileSuffixes: [],
|
|
66
|
+
removableFilePrefixes: [],
|
|
67
|
+
removableFilePatterns: [],
|
|
68
|
+
removableSpecificFiles: [],
|
|
69
|
+
},
|
|
70
|
+
deep: {
|
|
71
|
+
label: 'Deep clean (without node_modules)',
|
|
72
|
+
removeEmptyDirs: true,
|
|
73
|
+
removableDirNames: [...cacheDirNames, ...deepDirNames],
|
|
74
|
+
removableFileNames: cacheFileNames,
|
|
75
|
+
removableFileSuffixes: deepFileSuffixes,
|
|
76
|
+
removableFilePrefixes: deepFilePrefixes,
|
|
77
|
+
removableFilePatterns: deepFilePatterns,
|
|
78
|
+
removableSpecificFiles: [],
|
|
79
|
+
},
|
|
80
|
+
nuclear: {
|
|
81
|
+
label: 'Nuclear clean',
|
|
82
|
+
removeEmptyDirs: true,
|
|
83
|
+
removableDirNames: [...cacheDirNames, ...deepDirNames, 'node_modules'],
|
|
84
|
+
removableFileNames: cacheFileNames,
|
|
85
|
+
removableFileSuffixes: deepFileSuffixes,
|
|
86
|
+
removableFilePrefixes: deepFilePrefixes,
|
|
87
|
+
removableFilePatterns: deepFilePatterns,
|
|
88
|
+
removableSpecificFiles: [],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
export async function pathExists(target) {
|
|
94
|
+
try {
|
|
95
|
+
await access(target);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export async function findConfigPath(startDir) {
|
|
103
|
+
let current = path.resolve(startDir);
|
|
104
|
+
while (true) {
|
|
105
|
+
const candidate = path.join(current, configDirName, configFileName);
|
|
106
|
+
if (await pathExists(candidate))
|
|
107
|
+
return candidate;
|
|
108
|
+
const next = path.dirname(current);
|
|
109
|
+
if (next === current)
|
|
110
|
+
return null;
|
|
111
|
+
current = next;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function mergeLevelConfig(base, override) {
|
|
115
|
+
if (!override)
|
|
116
|
+
return { ...base };
|
|
117
|
+
return {
|
|
118
|
+
label: override.label ?? base.label,
|
|
119
|
+
removeEmptyDirs: override.removeEmptyDirs ?? base.removeEmptyDirs,
|
|
120
|
+
removableDirNames: override.removableDirNames ?? base.removableDirNames,
|
|
121
|
+
removableFileNames: override.removableFileNames ?? base.removableFileNames,
|
|
122
|
+
removableFileSuffixes: override.removableFileSuffixes ?? base.removableFileSuffixes,
|
|
123
|
+
removableFilePrefixes: override.removableFilePrefixes ?? base.removableFilePrefixes,
|
|
124
|
+
removableFilePatterns: override.removableFilePatterns ?? base.removableFilePatterns,
|
|
125
|
+
removableSpecificFiles: override.removableSpecificFiles ?? base.removableSpecificFiles,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export function mergeConfig(override = {}) {
|
|
129
|
+
const cleanerOverride = override.cleaner ?? {};
|
|
130
|
+
return {
|
|
131
|
+
packageManager: override.packageManager ?? defaultConfig.packageManager,
|
|
132
|
+
tasks: override.tasks ?? defaultConfig.tasks,
|
|
133
|
+
guards: override.guards,
|
|
134
|
+
documentation: override.documentation,
|
|
135
|
+
workspaceTests: override.workspaceTests,
|
|
136
|
+
repoCheck: override.repoCheck,
|
|
137
|
+
releaseGate: override.releaseGate,
|
|
138
|
+
validate: override.validate,
|
|
139
|
+
jsdocReport: override.jsdocReport,
|
|
140
|
+
bundleAudit: override.bundleAudit,
|
|
141
|
+
upgrade: override.upgrade,
|
|
142
|
+
devWatch: override.devWatch,
|
|
143
|
+
devGrid: override.devGrid,
|
|
144
|
+
environment: override.environment,
|
|
145
|
+
cleaner: {
|
|
146
|
+
workspaceRootNames: cleanerOverride.workspaceRootNames ?? defaultConfig.cleaner.workspaceRootNames,
|
|
147
|
+
protectedRootNames: cleanerOverride.protectedRootNames ?? defaultConfig.cleaner.protectedRootNames,
|
|
148
|
+
skipEmptyDirNames: cleanerOverride.skipEmptyDirNames ?? defaultConfig.cleaner.skipEmptyDirNames,
|
|
149
|
+
skipArtifactDirNames: cleanerOverride.skipArtifactDirNames ?? defaultConfig.cleaner.skipArtifactDirNames,
|
|
150
|
+
levels: {
|
|
151
|
+
empty: mergeLevelConfig(defaultConfig.cleaner.levels.empty, cleanerOverride.levels?.empty),
|
|
152
|
+
cache: mergeLevelConfig(defaultConfig.cleaner.levels.cache, cleanerOverride.levels?.cache),
|
|
153
|
+
deep: mergeLevelConfig(defaultConfig.cleaner.levels.deep, cleanerOverride.levels?.deep),
|
|
154
|
+
nuclear: mergeLevelConfig(defaultConfig.cleaner.levels.nuclear, cleanerOverride.levels?.nuclear),
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export async function loadConfig(cwd) {
|
|
160
|
+
const configPath = await findConfigPath(cwd);
|
|
161
|
+
if (!configPath)
|
|
162
|
+
return { config: mergeConfig(), configPath: null };
|
|
163
|
+
const raw = await readFile(configPath, 'utf8');
|
|
164
|
+
let parsed;
|
|
165
|
+
try {
|
|
166
|
+
parsed = JSON.parse(raw);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
throw new Error(`Invalid .webtoolkit-cli/config.json: ${error.message}`);
|
|
170
|
+
}
|
|
171
|
+
validateConfig(parsed);
|
|
172
|
+
return { config: mergeConfig(parsed), configPath };
|
|
173
|
+
}
|
package/dist/dev-grid.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { runCommandInherited } from './process.js';
|
|
6
|
+
const WINDOWS_TERMINAL_FRAGMENT_APP_NAME = 'WebToolkit.Cli';
|
|
7
|
+
const WINDOWS_TERMINAL_FRAGMENT_FILE_NAME = 'dev-grid.json';
|
|
8
|
+
function getPowerShellExecutable() {
|
|
9
|
+
const pwshResult = spawnSync('where.exe', ['pwsh'], { stdio: 'ignore', windowsHide: true });
|
|
10
|
+
return pwshResult.status === 0 ? 'pwsh' : 'powershell.exe';
|
|
11
|
+
}
|
|
12
|
+
function hasWindowsTerminal() {
|
|
13
|
+
const result = spawnSync('where.exe', ['wt.exe'], { stdio: 'ignore', windowsHide: true });
|
|
14
|
+
return result.status === 0;
|
|
15
|
+
}
|
|
16
|
+
function createPaneCommand(command) {
|
|
17
|
+
return [getPowerShellExecutable(), '-NoLogo', '-NoExit', '-Command', command];
|
|
18
|
+
}
|
|
19
|
+
function createPaneArgs(repoRoot, subcommand, orientation, pane, silent, profileName, size) {
|
|
20
|
+
const args = [subcommand];
|
|
21
|
+
if (orientation)
|
|
22
|
+
args.push(orientation);
|
|
23
|
+
if (size !== undefined)
|
|
24
|
+
args.push('--size', String(Number(size.toFixed(6))));
|
|
25
|
+
if (profileName)
|
|
26
|
+
args.push('--profile', profileName);
|
|
27
|
+
args.push('--startingDirectory', repoRoot, '--title', pane.title, '--suppressApplicationTitle', ...createPaneCommand(silent ? pane.silentCommand ?? pane.command : pane.command));
|
|
28
|
+
return args;
|
|
29
|
+
}
|
|
30
|
+
function getValidatedDevGridRows(rows) {
|
|
31
|
+
if (!rows.length)
|
|
32
|
+
throw new Error('devGrid.layout.rows is not configured.');
|
|
33
|
+
for (const [rowIndex, row] of rows.entries()) {
|
|
34
|
+
if (!row.panes.length)
|
|
35
|
+
throw new Error(`devGrid.layout.rows[${rowIndex}].panes is not configured.`);
|
|
36
|
+
for (const pane of row.panes) {
|
|
37
|
+
if (pane.fontSize === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
if (!Number.isInteger(pane.fontSize) || pane.fontSize <= 0) {
|
|
40
|
+
throw new Error(`devGrid pane "${pane.title}" has invalid fontSize ${String(pane.fontSize)}. Use a positive integer.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return rows;
|
|
45
|
+
}
|
|
46
|
+
function getWindowsTerminalFragmentFilePath() {
|
|
47
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
48
|
+
if (!localAppData) {
|
|
49
|
+
throw new Error('LOCALAPPDATA is not defined. Cannot configure Windows Terminal fragment profiles for devGrid.');
|
|
50
|
+
}
|
|
51
|
+
return join(localAppData, 'Microsoft', 'Windows Terminal', 'Fragments', WINDOWS_TERMINAL_FRAGMENT_APP_NAME, WINDOWS_TERMINAL_FRAGMENT_FILE_NAME);
|
|
52
|
+
}
|
|
53
|
+
function buildPaneProfileName(repoRoot, pane) {
|
|
54
|
+
const hash = createHash('sha1')
|
|
55
|
+
.update(`${repoRoot}\n${pane.title}\n${String(pane.fontSize)}`)
|
|
56
|
+
.digest('hex')
|
|
57
|
+
.slice(0, 12);
|
|
58
|
+
return `WebToolkit Dev Grid ${hash}`;
|
|
59
|
+
}
|
|
60
|
+
function preparePaneProfiles(repoRoot, panes) {
|
|
61
|
+
const panesWithFontSize = panes.filter((pane) => pane.fontSize !== undefined);
|
|
62
|
+
if (!panesWithFontSize.length)
|
|
63
|
+
return new Map();
|
|
64
|
+
const fragmentFilePath = getWindowsTerminalFragmentFilePath();
|
|
65
|
+
mkdirSync(dirname(fragmentFilePath), { recursive: true });
|
|
66
|
+
const profiles = panesWithFontSize.map((pane) => ({
|
|
67
|
+
name: buildPaneProfileName(repoRoot, pane),
|
|
68
|
+
hidden: true,
|
|
69
|
+
commandline: getPowerShellExecutable(),
|
|
70
|
+
fontSize: pane.fontSize,
|
|
71
|
+
}));
|
|
72
|
+
writeFileSync(fragmentFilePath, `${JSON.stringify({ profiles }, null, 2)}\n`, 'utf8');
|
|
73
|
+
return new Map(profiles.map((profile, index) => [panesWithFontSize[index], profile.name]));
|
|
74
|
+
}
|
|
75
|
+
function createWindowsTerminalCommands(repoRoot, rows, silent, windowName, paneProfiles) {
|
|
76
|
+
const firstPane = rows[0].panes[0];
|
|
77
|
+
const commands = [
|
|
78
|
+
['--window', windowName, '--maximized', ...createPaneArgs(repoRoot, 'new-tab', null, firstPane, silent, paneProfiles.get(firstPane))],
|
|
79
|
+
];
|
|
80
|
+
for (let rowIndex = 1; rowIndex < rows.length; rowIndex += 1) {
|
|
81
|
+
const pane = rows[rowIndex].panes[0];
|
|
82
|
+
const remainingRows = rows.length - rowIndex;
|
|
83
|
+
commands.push([
|
|
84
|
+
'--window',
|
|
85
|
+
windowName,
|
|
86
|
+
...createPaneArgs(repoRoot, 'split-pane', '--horizontal', pane, silent, paneProfiles.get(pane), remainingRows / (remainingRows + 1)),
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
if (rows.length > 1)
|
|
90
|
+
commands.push(['--window', windowName, 'move-focus', 'first']);
|
|
91
|
+
for (const [rowIndex, row] of rows.entries()) {
|
|
92
|
+
for (let paneIndex = 1; paneIndex < row.panes.length; paneIndex += 1) {
|
|
93
|
+
const pane = row.panes[paneIndex];
|
|
94
|
+
const remainingPanes = row.panes.length - paneIndex;
|
|
95
|
+
commands.push([
|
|
96
|
+
'--window',
|
|
97
|
+
windowName,
|
|
98
|
+
...createPaneArgs(repoRoot, 'split-pane', '--vertical', pane, silent, paneProfiles.get(pane), remainingPanes / (remainingPanes + 1)),
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
if (rowIndex < rows.length - 1)
|
|
102
|
+
commands.push(['--window', windowName, 'move-focus', 'down']);
|
|
103
|
+
}
|
|
104
|
+
return commands;
|
|
105
|
+
}
|
|
106
|
+
function runStep(runtime, step) {
|
|
107
|
+
if (!step.command) {
|
|
108
|
+
throw new Error(`Dev grid step "${step.label}" must define command.`);
|
|
109
|
+
}
|
|
110
|
+
const code = runCommandInherited({
|
|
111
|
+
command: step.command,
|
|
112
|
+
args: step.args ?? [],
|
|
113
|
+
cwd: step.cwd,
|
|
114
|
+
env: step.env,
|
|
115
|
+
}, runtime.cwd);
|
|
116
|
+
if (code !== 0)
|
|
117
|
+
process.exit(code);
|
|
118
|
+
}
|
|
119
|
+
function runFallback(runtime, script, reason, dryRun) {
|
|
120
|
+
const normalizedScript = normalizeFallbackScript(script);
|
|
121
|
+
if (dryRun) {
|
|
122
|
+
process.stdout.write(`${JSON.stringify({ executable: runtime.config.packageManager, args: ['run', normalizedScript], reason }, null, 2)}\n`);
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
process.stderr.write(`${reason} Falling back to \`${runtime.config.packageManager} run ${normalizedScript}\`.\n`);
|
|
126
|
+
const executable = process.platform === 'win32' ? `${runtime.config.packageManager}.cmd` : runtime.config.packageManager;
|
|
127
|
+
const result = spawnSync(executable, ['run', normalizedScript], { cwd: runtime.cwd, stdio: 'inherit' });
|
|
128
|
+
if (result.error)
|
|
129
|
+
throw result.error;
|
|
130
|
+
process.exit(result.status ?? 1);
|
|
131
|
+
}
|
|
132
|
+
function normalizeFallbackScript(script) {
|
|
133
|
+
const normalized = script.trim().replace(/\s+/gu, ' ');
|
|
134
|
+
const match = normalized.match(/^(?:npm|pnpm|yarn) run (.+)$/u);
|
|
135
|
+
return match ? match[1] : normalized;
|
|
136
|
+
}
|
|
137
|
+
function sleep(milliseconds) {
|
|
138
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
139
|
+
}
|
|
140
|
+
export function runDevGrid(runtime, rawArgs) {
|
|
141
|
+
const config = runtime.config.devGrid;
|
|
142
|
+
if (!config?.layout?.rows?.length)
|
|
143
|
+
throw new Error('devGrid.layout.rows is not configured.');
|
|
144
|
+
const rows = getValidatedDevGridRows(config.layout.rows);
|
|
145
|
+
const panes = rows.flatMap((row) => row.panes);
|
|
146
|
+
const silent = rawArgs.includes('--silent');
|
|
147
|
+
const dryRun = rawArgs.includes('--dry-run');
|
|
148
|
+
const fallbackScript = silent ? config.silentFallbackScript ?? config.fallbackScript : config.fallbackScript;
|
|
149
|
+
if (process.platform !== 'win32') {
|
|
150
|
+
if (!fallbackScript)
|
|
151
|
+
throw new Error('Windows Terminal grid is unavailable and no devGrid.fallbackScript is configured.');
|
|
152
|
+
runFallback(runtime, fallbackScript, 'Windows Terminal grid is unavailable on this platform.', dryRun);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!dryRun && config.preflightCommand)
|
|
156
|
+
runStep(runtime, config.preflightCommand);
|
|
157
|
+
if (!hasWindowsTerminal()) {
|
|
158
|
+
if (!fallbackScript)
|
|
159
|
+
throw new Error('Windows Terminal (`wt.exe`) is unavailable and no devGrid.fallbackScript is configured.');
|
|
160
|
+
runFallback(runtime, fallbackScript, 'Windows Terminal (`wt.exe`) is not available.', dryRun);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const windowName = `webtoolkit-dev-grid-${Date.now()}-${process.pid}`;
|
|
164
|
+
const paneProfiles = preparePaneProfiles(runtime.cwd, panes);
|
|
165
|
+
const commands = createWindowsTerminalCommands(runtime.cwd, rows, silent, windowName, paneProfiles);
|
|
166
|
+
if (dryRun) {
|
|
167
|
+
process.stdout.write(`${JSON.stringify({ executable: 'wt.exe', commands }, null, 2)}\n`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
for (const [index, commandArgs] of commands.entries()) {
|
|
171
|
+
const result = spawnSync('wt.exe', commandArgs, { cwd: runtime.cwd, windowsHide: true, stdio: 'pipe', encoding: 'utf8' });
|
|
172
|
+
if (result.error)
|
|
173
|
+
throw result.error;
|
|
174
|
+
if (result.status !== 0) {
|
|
175
|
+
const details = result.stderr?.trim() || result.stdout?.trim() || `exit code ${result.status}`;
|
|
176
|
+
throw new Error(`Windows Terminal command failed: ${details}`);
|
|
177
|
+
}
|
|
178
|
+
if (index < commands.length - 1)
|
|
179
|
+
sleep(250);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WebToolkitCliConfig } from './config.js';
|
|
2
|
+
type Runtime = {
|
|
3
|
+
cwd: string;
|
|
4
|
+
config: WebToolkitCliConfig;
|
|
5
|
+
};
|
|
6
|
+
export declare function resolveDevWatchSpawnSpec(command: string, commandArgs: string[]): {
|
|
7
|
+
command: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
detached: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function parseWindowsNetstatListeningPids(output: string, port: number): number[];
|
|
12
|
+
export declare function runDevWatch(runtime: Runtime, rawArgs: string[]): Promise<void>;
|
|
13
|
+
export {};
|