knip 6.39.0 → 6.41.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/dist/ConfigurationChief.d.ts +24 -0
- package/dist/DependencyDeputy.d.ts +1 -0
- package/dist/DependencyDeputy.js +28 -2
- package/dist/IssueCollector.js +13 -15
- package/dist/IssueFixer.js +1 -1
- package/dist/WorkspaceWorker.d.ts +3 -4
- package/dist/WorkspaceWorker.js +25 -45
- package/dist/binaries/bash-parser.js +10 -4
- package/dist/binaries/command.d.ts +1 -1
- package/dist/binaries/command.js +14 -8
- package/dist/compilers/compilers.js +1 -1
- package/dist/graph/build.js +33 -17
- package/dist/manifest/index.d.ts +1 -0
- package/dist/manifest/index.js +5 -0
- package/dist/plugins/capacitor/index.js +1 -1
- package/dist/plugins/commitlint/index.js +34 -9
- package/dist/plugins/eslint/resolveFromAST.d.ts +0 -1
- package/dist/plugins/eslint/resolveFromAST.js +1 -1
- package/dist/plugins/expo/helpers.js +1 -1
- package/dist/plugins/expo/index.js +4 -24
- package/dist/plugins/expo-router/index.d.ts +3 -0
- package/dist/plugins/expo-router/index.js +32 -0
- package/dist/plugins/flue/index.d.ts +3 -0
- package/dist/plugins/flue/index.js +42 -0
- package/dist/plugins/index.d.ts +4 -0
- package/dist/plugins/index.js +8 -0
- package/dist/plugins/jasmine/index.d.ts +3 -0
- package/dist/plugins/jasmine/index.js +55 -0
- package/dist/plugins/jasmine/types.d.ts +7 -0
- package/dist/plugins/jasmine/types.js +1 -0
- package/dist/plugins/jest/helpers.d.ts +1 -0
- package/dist/plugins/jest/helpers.js +2 -0
- package/dist/plugins/jest/index.js +20 -11
- package/dist/plugins/mise/scripts.js +32 -10
- package/dist/plugins/moonrepo/index.js +2 -1
- package/dist/plugins/nx/index.js +6 -2
- package/dist/plugins/osls/index.d.ts +3 -0
- package/dist/plugins/osls/index.js +13 -0
- package/dist/plugins/oxlint/index.d.ts +3 -0
- package/dist/plugins/oxlint/index.js +4 -31
- package/dist/plugins/oxlint/types.d.ts +1 -0
- package/dist/plugins/playwright/index.js +17 -0
- package/dist/plugins/playwright/types.d.ts +5 -0
- package/dist/plugins/postcss/index.js +5 -1
- package/dist/plugins/react-native/index.js +1 -1
- package/dist/plugins/rsbuild/index.js +58 -6
- package/dist/plugins/rsbuild/types.d.ts +24 -1
- package/dist/plugins/rspack/index.d.ts +3 -1
- package/dist/plugins/rspack/index.js +1 -1
- package/dist/plugins/rstest/types.d.ts +1 -5
- package/dist/plugins/serverless-framework/index.d.ts +4 -1
- package/dist/plugins/serverless-framework/index.js +40 -7
- package/dist/plugins/serverless-framework/types.d.ts +4 -2
- package/dist/plugins/simple-git-hooks/index.js +1 -1
- package/dist/plugins/sveltejs-package/helpers.js +7 -44
- package/dist/plugins/vitest/index.js +16 -2
- package/dist/plugins/vitest/types.d.ts +2 -0
- package/dist/run.js +2 -0
- package/dist/schema/configuration.d.ts +60 -0
- package/dist/schema/plugins.d.ts +20 -0
- package/dist/schema/plugins.js +4 -0
- package/dist/types/PluginNames.d.ts +2 -2
- package/dist/types/PluginNames.js +4 -0
- package/dist/types/workspace.d.ts +1 -0
- package/dist/typescript/ast-nodes.js +291 -0
- package/dist/typescript/resolve-module-names.js +15 -4
- package/dist/typescript/visitors/walk.js +12 -0
- package/dist/util/create-options.d.ts +40 -0
- package/dist/util/filter-transitive-dependencies.d.ts +2 -0
- package/dist/util/filter-transitive-dependencies.js +38 -0
- package/dist/util/glob-cache.d.ts +2 -2
- package/dist/util/glob-core.js +70 -47
- package/dist/util/glob.js +15 -10
- package/dist/util/load-tsconfig.d.ts +1 -1
- package/dist/util/load-tsconfig.js +21 -11
- package/dist/util/module-graph.js +9 -4
- package/dist/util/package-json.d.ts +1 -354
- package/dist/util/package-json.js +56 -10
- package/dist/util/scripts.d.ts +3 -2
- package/dist/util/scripts.js +9 -3
- package/dist/util/to-source-path.d.ts +1 -1
- package/dist/util/to-source-path.js +7 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -10
- package/schema.json +16 -0
|
@@ -237,6 +237,11 @@ export declare class ConfigurationChief {
|
|
|
237
237
|
entry?: string | string[] | undefined;
|
|
238
238
|
project?: string | string[] | undefined;
|
|
239
239
|
} | undefined;
|
|
240
|
+
'expo-router'?: string | boolean | string[] | {
|
|
241
|
+
config?: string | string[] | undefined;
|
|
242
|
+
entry?: string | string[] | undefined;
|
|
243
|
+
project?: string | string[] | undefined;
|
|
244
|
+
} | undefined;
|
|
240
245
|
'expressive-code'?: string | boolean | string[] | {
|
|
241
246
|
config?: string | string[] | undefined;
|
|
242
247
|
entry?: string | string[] | undefined;
|
|
@@ -247,6 +252,11 @@ export declare class ConfigurationChief {
|
|
|
247
252
|
entry?: string | string[] | undefined;
|
|
248
253
|
project?: string | string[] | undefined;
|
|
249
254
|
} | undefined;
|
|
255
|
+
flue?: string | boolean | string[] | {
|
|
256
|
+
config?: string | string[] | undefined;
|
|
257
|
+
entry?: string | string[] | undefined;
|
|
258
|
+
project?: string | string[] | undefined;
|
|
259
|
+
} | undefined;
|
|
250
260
|
fumadocs?: string | boolean | string[] | {
|
|
251
261
|
config?: string | string[] | undefined;
|
|
252
262
|
entry?: string | string[] | undefined;
|
|
@@ -292,6 +302,11 @@ export declare class ConfigurationChief {
|
|
|
292
302
|
entry?: string | string[] | undefined;
|
|
293
303
|
project?: string | string[] | undefined;
|
|
294
304
|
} | undefined;
|
|
305
|
+
jasmine?: string | boolean | string[] | {
|
|
306
|
+
config?: string | string[] | undefined;
|
|
307
|
+
entry?: string | string[] | undefined;
|
|
308
|
+
project?: string | string[] | undefined;
|
|
309
|
+
} | undefined;
|
|
295
310
|
jest?: string | boolean | string[] | {
|
|
296
311
|
config?: string | string[] | undefined;
|
|
297
312
|
entry?: string | string[] | undefined;
|
|
@@ -497,6 +512,11 @@ export declare class ConfigurationChief {
|
|
|
497
512
|
entry?: string | string[] | undefined;
|
|
498
513
|
project?: string | string[] | undefined;
|
|
499
514
|
} | undefined;
|
|
515
|
+
osls?: string | boolean | string[] | {
|
|
516
|
+
config?: string | string[] | undefined;
|
|
517
|
+
entry?: string | string[] | undefined;
|
|
518
|
+
project?: string | string[] | undefined;
|
|
519
|
+
} | undefined;
|
|
500
520
|
oxfmt?: string | boolean | string[] | {
|
|
501
521
|
config?: string | string[] | undefined;
|
|
502
522
|
entry?: string | string[] | undefined;
|
|
@@ -1066,8 +1086,10 @@ export declare class ConfigurationChief {
|
|
|
1066
1086
|
eve?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1067
1087
|
execa?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1068
1088
|
expo?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1089
|
+
"expo-router"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1069
1090
|
"expressive-code"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1070
1091
|
fast?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1092
|
+
flue?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1071
1093
|
fumadocs?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1072
1094
|
gatsby?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1073
1095
|
"github-action"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
@@ -1077,6 +1099,7 @@ export declare class ConfigurationChief {
|
|
|
1077
1099
|
hardhat?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1078
1100
|
husky?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1079
1101
|
"i18next-parser"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1102
|
+
jasmine?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1080
1103
|
jest?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1081
1104
|
karma?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1082
1105
|
knex?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
@@ -1118,6 +1141,7 @@ export declare class ConfigurationChief {
|
|
|
1118
1141
|
"openapi-ts"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1119
1142
|
openclaw?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1120
1143
|
orval?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1144
|
+
osls?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1121
1145
|
oxfmt?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1122
1146
|
oxlint?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
1123
1147
|
"panda-css"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
|
|
@@ -40,6 +40,7 @@ export declare class DependencyDeputy {
|
|
|
40
40
|
peerDependencies: DependencySet;
|
|
41
41
|
optionalPeerDependencies: DependencySet;
|
|
42
42
|
requiredPeerDependencies: DependencyArray;
|
|
43
|
+
uninstalledOptionalDependencies: DependencySet;
|
|
43
44
|
allDependencies: DependencySet;
|
|
44
45
|
engines: Record<string, string>;
|
|
45
46
|
isPrivate: boolean;
|
package/dist/DependencyDeputy.js
CHANGED
|
@@ -38,15 +38,25 @@ export class DependencyDeputy {
|
|
|
38
38
|
const requiredPeerDependencies = peerDependencies.filter(dep => !optionalPeerDependencies.has(dep));
|
|
39
39
|
const devDependencies = Object.keys(manifest.devDependencies ?? {});
|
|
40
40
|
const allDependencies = [...dependencies, ...devDependencies, ...peerDependencies, ...optionalDependencies];
|
|
41
|
-
const
|
|
41
|
+
const uninstalledOptionalDependencies = new Set();
|
|
42
|
+
const packageNames = [
|
|
43
|
+
...dependencies,
|
|
44
|
+
...peerDependencies,
|
|
45
|
+
...optionalDependencies,
|
|
46
|
+
...(this.isProduction ? [] : devDependencies),
|
|
47
|
+
];
|
|
42
48
|
if (this.isReportDependencies) {
|
|
43
|
-
const { hostDependencies, installedBinaries, hasTypesIncluded } = getDependencyMetaData({
|
|
49
|
+
const { hostDependencies, installedBinaries, hasTypesIncluded, uninstalledDependencies } = getDependencyMetaData({
|
|
44
50
|
packageNames,
|
|
45
51
|
dir,
|
|
46
52
|
});
|
|
47
53
|
this.setHostDependencies(name, hostDependencies);
|
|
48
54
|
this.installedBinaries.set(name, installedBinaries);
|
|
49
55
|
this.hasTypesIncluded.set(name, hasTypesIncluded);
|
|
56
|
+
for (const dependency of optionalDependencies) {
|
|
57
|
+
if (uninstalledDependencies.has(dependency))
|
|
58
|
+
uninstalledOptionalDependencies.add(dependency);
|
|
59
|
+
}
|
|
50
60
|
}
|
|
51
61
|
const ignoreDependencies = id.flatMap(id => filterIsProduction(id, this.isProduction)).map(toRegexOrString);
|
|
52
62
|
const ignoreBinaries = ib.flatMap(ib => filterIsProduction(ib, this.isProduction)).map(toRegexOrString);
|
|
@@ -66,6 +76,7 @@ export class DependencyDeputy {
|
|
|
66
76
|
peerDependencies: new Set(peerDependencies),
|
|
67
77
|
optionalPeerDependencies,
|
|
68
78
|
requiredPeerDependencies,
|
|
79
|
+
uninstalledOptionalDependencies,
|
|
69
80
|
allDependencies: new Set(allDependencies),
|
|
70
81
|
engines: manifest.engines ?? {},
|
|
71
82
|
isPrivate: Boolean(manifest.private),
|
|
@@ -171,6 +182,21 @@ export class DependencyDeputy {
|
|
|
171
182
|
}
|
|
172
183
|
}
|
|
173
184
|
}
|
|
185
|
+
const optionalDependencies = new Set();
|
|
186
|
+
for (const name of workspaceNames) {
|
|
187
|
+
const manifest = this._manifests.get(name);
|
|
188
|
+
if (!manifest)
|
|
189
|
+
continue;
|
|
190
|
+
for (const dependency of manifest.uninstalledOptionalDependencies) {
|
|
191
|
+
const defaultBinaryName = dependency.replace(/^@[^/]+\//, '');
|
|
192
|
+
if (defaultBinaryName === binaryName) {
|
|
193
|
+
optionalDependencies.add(dependency);
|
|
194
|
+
this.addReferencedDependency(name, dependency);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (optionalDependencies.size)
|
|
199
|
+
return optionalDependencies;
|
|
174
200
|
return;
|
|
175
201
|
}
|
|
176
202
|
isInDependencies(workspaceName, packageName, isDevOnly) {
|
package/dist/IssueCollector.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import picomatch from 'picomatch';
|
|
2
2
|
import { ROOT_WORKSPACE_NAME } from './constants.js';
|
|
3
3
|
import { partition } from './util/array.js';
|
|
4
|
-
import { prependDirToPattern } from './util/glob.js';
|
|
5
4
|
import { initCounters, initIssues } from './util/issue-initializers.js';
|
|
6
5
|
import { relative } from './util/path.js';
|
|
7
6
|
const createMatcher = (patterns) => {
|
|
@@ -82,23 +81,22 @@ export class IssueCollector {
|
|
|
82
81
|
return;
|
|
83
82
|
const issueTypePatterns = new Map();
|
|
84
83
|
for (const [pattern, issueTypes] of Object.entries(ignoreIssues)) {
|
|
85
|
-
const id = prependDirToPattern(this.cwd, pattern);
|
|
86
84
|
for (const issueType of issueTypes) {
|
|
87
85
|
if (!issueTypePatterns.has(issueType)) {
|
|
88
86
|
issueTypePatterns.set(issueType, []);
|
|
89
87
|
}
|
|
90
|
-
issueTypePatterns.get(issueType)?.push(
|
|
88
|
+
issueTypePatterns.get(issueType)?.push(pattern);
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
91
|
for (const [issueType, patterns] of issueTypePatterns) {
|
|
94
92
|
this.issueMatchers.set(issueType, picomatch(patterns, { dot: true }));
|
|
95
93
|
}
|
|
96
94
|
}
|
|
97
|
-
shouldIgnoreIssue(
|
|
95
|
+
shouldIgnoreIssue(relativePath, issueType) {
|
|
98
96
|
const matcher = this.issueMatchers.get(issueType);
|
|
99
97
|
if (!matcher)
|
|
100
98
|
return false;
|
|
101
|
-
return matcher(
|
|
99
|
+
return matcher(relativePath);
|
|
102
100
|
}
|
|
103
101
|
addFileCounts({ processed, unused }) {
|
|
104
102
|
this.counters.processed += processed;
|
|
@@ -110,17 +108,17 @@ export class IssueCollector {
|
|
|
110
108
|
continue;
|
|
111
109
|
if (this.referencedFiles.has(filePath))
|
|
112
110
|
continue;
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const symbol = relative(this.cwd, filePath);
|
|
112
|
+
if (this.isMatch(symbol)) {
|
|
113
|
+
this.markUsedPatterns(symbol, this.unusedIgnorePatterns);
|
|
115
114
|
continue;
|
|
116
115
|
}
|
|
117
|
-
if (this.isFileMatch(
|
|
118
|
-
this.markUsedPatterns(
|
|
116
|
+
if (this.isFileMatch(symbol)) {
|
|
117
|
+
this.markUsedPatterns(symbol, this.unusedIgnoreFilesPatterns);
|
|
119
118
|
continue;
|
|
120
119
|
}
|
|
121
|
-
if (this.shouldIgnoreIssue(
|
|
120
|
+
if (this.shouldIgnoreIssue(symbol, 'files'))
|
|
122
121
|
continue;
|
|
123
|
-
const symbol = relative(this.cwd, filePath);
|
|
124
122
|
this.issues.files[symbol] = {
|
|
125
123
|
[symbol]: { type: 'files', filePath, symbol, workspace: '', severity: this.rules.files, fixes: [] },
|
|
126
124
|
};
|
|
@@ -131,15 +129,15 @@ export class IssueCollector {
|
|
|
131
129
|
addIssue(issue) {
|
|
132
130
|
if (!this.workspaceFilter(issue.filePath))
|
|
133
131
|
return;
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
const key = relative(this.cwd, issue.filePath);
|
|
133
|
+
if (this.isMatch(key)) {
|
|
134
|
+
this.markUsedPatterns(key, this.unusedIgnorePatterns);
|
|
136
135
|
return;
|
|
137
136
|
}
|
|
138
|
-
if (this.shouldIgnoreIssue(
|
|
137
|
+
if (this.shouldIgnoreIssue(key, issue.type))
|
|
139
138
|
return;
|
|
140
139
|
if (this.rules[issue.type] === 'off')
|
|
141
140
|
return;
|
|
142
|
-
const key = relative(this.cwd, issue.filePath);
|
|
143
141
|
issue.severity = this.rules[issue.type];
|
|
144
142
|
const issues = this.issues[issue.type];
|
|
145
143
|
issues[key] = issues[key] ?? {};
|
package/dist/IssueFixer.js
CHANGED
|
@@ -21,7 +21,7 @@ export const fix = async (issues, counters, options) => {
|
|
|
21
21
|
}
|
|
22
22
|
if (options.isFormat) {
|
|
23
23
|
const report = await formatly(Array.from(touchedFiles));
|
|
24
|
-
if (report.ran && report.result
|
|
24
|
+
if (report.ran && report.result.runner !== 'dry-run' && report.result.code === 0) {
|
|
25
25
|
debugLogArray('*', `Formatted files using ${report.formatter.name} (${report.formatter.runner})`, touchedFiles);
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
@@ -49,7 +49,7 @@ export declare class WorkspaceWorker {
|
|
|
49
49
|
enabledPluginsMap: Partial<Record<PluginName, boolean>>;
|
|
50
50
|
enabledPlugins: PluginName[];
|
|
51
51
|
enabledPluginsInAncestors: string[];
|
|
52
|
-
cache: CacheConsultant<CacheItem
|
|
52
|
+
cache: CacheConsultant<Partial<Record<PluginName, CacheItem>>>;
|
|
53
53
|
configFilesMap: Map<string, Map<PluginName, Set<string>>>;
|
|
54
54
|
constructor({ name, dir, config, manifest, dependencies, rootManifest, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, handleInput, handleConfigLoadError, findWorkspaceByFilePath, getManifest, readRawFile, configFilesMap, options, }: WorkspaceManagerOptions);
|
|
55
55
|
init(): Promise<void>;
|
|
@@ -61,14 +61,13 @@ export declare class WorkspaceWorker {
|
|
|
61
61
|
private getPluginConfig;
|
|
62
62
|
getPluginConfigPatterns(): string[];
|
|
63
63
|
getPluginEntryFilePatterns(patterns: string[]): string[];
|
|
64
|
-
getProductionEntryFilePatterns(
|
|
65
|
-
getProductionProjectFilePatterns(
|
|
64
|
+
getProductionEntryFilePatterns(): string[];
|
|
65
|
+
getProductionProjectFilePatterns(): string[];
|
|
66
66
|
private getConfigurationFilePatterns;
|
|
67
67
|
registerCompilers(registerCompiler: RegisterCompiler): Promise<void>;
|
|
68
68
|
resolveSourceMaps(): Promise<SourceMap[]>;
|
|
69
69
|
registerVisitors(options: RegisterVisitorsOptions): void;
|
|
70
70
|
runPlugins(): Promise<Input[]>;
|
|
71
|
-
private filterTransitiveDependencies;
|
|
72
71
|
getConfigurationHints(type: 'entry' | 'project', patterns: string[], filePaths: string[], includedPaths: Set<string>, compilerExtensions?: Set<string>): ConfigurationHint[];
|
|
73
72
|
onDispose(): void;
|
|
74
73
|
}
|
package/dist/WorkspaceWorker.js
CHANGED
|
@@ -6,15 +6,15 @@ import { DEFAULT_EXTENSIONS, ROOT_WORKSPACE_NAME } from './constants.js';
|
|
|
6
6
|
import { getFilteredScripts } from './manifest/helpers.js';
|
|
7
7
|
import { PluginEntries, Plugins } from './plugins.js';
|
|
8
8
|
import { createManifest } from './util/package-json.js';
|
|
9
|
-
import {
|
|
9
|
+
import { isExternalReExportsOnly } from './typescript/ast-helpers.js';
|
|
10
10
|
import { _parseFile } from './typescript/ast-nodes.js';
|
|
11
11
|
import { compact } from './util/array.js';
|
|
12
12
|
import { debugLogArray, debugLogObject } from './util/debug.js';
|
|
13
13
|
import { _glob, hasNoProductionSuffix, hasProductionSuffix, negate } from './util/glob.js';
|
|
14
|
-
import { isCatalog, isConfig,
|
|
15
|
-
import {
|
|
14
|
+
import { isCatalog, isConfig, toConfig, toDebugString, toEntry, toProductionEntry, } from './util/input.js';
|
|
15
|
+
import { filterTransitiveDependencies } from './util/filter-transitive-dependencies.js';
|
|
16
16
|
import { timerify } from './util/Performance.js';
|
|
17
|
-
import { basename, dirname,
|
|
17
|
+
import { basename, dirname, join, relative, toRelative } from './util/path.js';
|
|
18
18
|
import { extractPatternExtensions } from './util/pattern-extensions.js';
|
|
19
19
|
import { formatCauseMessage } from './util/errors.js';
|
|
20
20
|
import { logError } from './util/log.js';
|
|
@@ -132,17 +132,17 @@ export class WorkspaceWorker {
|
|
|
132
132
|
const negateWorkspaces = patterns.some(pattern => pattern.startsWith('**/')) ? this.negatedWorkspacePatterns : [];
|
|
133
133
|
return [patterns, negateWorkspaces, this.ignoredWorkspacePatterns.map(negate)].flat();
|
|
134
134
|
}
|
|
135
|
-
getProductionEntryFilePatterns(
|
|
135
|
+
getProductionEntryFilePatterns() {
|
|
136
136
|
const entry = this.config.entry.filter(hasProductionSuffix);
|
|
137
137
|
if (entry.length === 0)
|
|
138
138
|
return [];
|
|
139
139
|
const negatedEntryFiles = this.config.entry.filter(hasNoProductionSuffix).map(negate);
|
|
140
|
-
return [entry, negatedEntryFiles,
|
|
140
|
+
return [entry, negatedEntryFiles, this.negatedWorkspacePatterns].flat();
|
|
141
141
|
}
|
|
142
|
-
getProductionProjectFilePatterns(
|
|
142
|
+
getProductionProjectFilePatterns() {
|
|
143
143
|
const project = this.config.project;
|
|
144
144
|
if (project.length === 0)
|
|
145
|
-
return this.getProductionEntryFilePatterns(
|
|
145
|
+
return this.getProductionEntryFilePatterns();
|
|
146
146
|
const _project = this.config.project.map(pattern => {
|
|
147
147
|
if (!(pattern.endsWith('!') || pattern.startsWith('!')))
|
|
148
148
|
return negate(pattern);
|
|
@@ -156,7 +156,6 @@ export class WorkspaceWorker {
|
|
|
156
156
|
negatedEntryFiles,
|
|
157
157
|
negatedPluginConfigPatterns,
|
|
158
158
|
negatedPluginProjectFilePatterns,
|
|
159
|
-
negatedTestFilePatterns,
|
|
160
159
|
this.negatedWorkspacePatterns,
|
|
161
160
|
].flat();
|
|
162
161
|
}
|
|
@@ -326,8 +325,8 @@ export class WorkspaceWorker {
|
|
|
326
325
|
seenConfigFiles.add(configFilePath);
|
|
327
326
|
const isManifest = basename(configFilePath) === 'package.json';
|
|
328
327
|
const fd = isManifest ? undefined : this.cache.getFileDescriptor(configFilePath);
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
const data = fd?.changed ? undefined : fd?.meta?.data?.[pluginName];
|
|
329
|
+
if (data) {
|
|
331
330
|
if (data.resolveConfig)
|
|
332
331
|
for (const id of data.resolveConfig)
|
|
333
332
|
addInput(id, configFilePath);
|
|
@@ -370,7 +369,7 @@ export class WorkspaceWorker {
|
|
|
370
369
|
if (localConfig) {
|
|
371
370
|
const inputs = await plugin.resolveConfig(localConfig, resolveOpts);
|
|
372
371
|
if (plugin.isFilterTransitiveDependencies && !isManifest) {
|
|
373
|
-
|
|
372
|
+
filterTransitiveDependencies(inputs, configFilePath, this.readRawFile);
|
|
374
373
|
}
|
|
375
374
|
for (const input of inputs)
|
|
376
375
|
addInput(input, configFilePath);
|
|
@@ -405,7 +404,7 @@ export class WorkspaceWorker {
|
|
|
405
404
|
this.cache.removeEntry(configFilePath);
|
|
406
405
|
}
|
|
407
406
|
else if (fd?.meta) {
|
|
408
|
-
fd.meta.data = cache;
|
|
407
|
+
fd.meta.data = { ...fd.meta.data, [pluginName]: cache };
|
|
409
408
|
}
|
|
410
409
|
}
|
|
411
410
|
}
|
|
@@ -458,31 +457,6 @@ export class WorkspaceWorker {
|
|
|
458
457
|
debugLogArray(wsName, 'Plugin dependencies', () => compact(inputs.map(input => toDebugString(input, rootCwd))));
|
|
459
458
|
return inputs;
|
|
460
459
|
}
|
|
461
|
-
filterTransitiveDependencies(inputs, configFilePath) {
|
|
462
|
-
const literals = new Set();
|
|
463
|
-
const visited = new Set();
|
|
464
|
-
const collect = (filePath) => {
|
|
465
|
-
if (visited.has(filePath))
|
|
466
|
-
return;
|
|
467
|
-
visited.add(filePath);
|
|
468
|
-
const sourceText = this.readRawFile(filePath);
|
|
469
|
-
if (!sourceText)
|
|
470
|
-
return;
|
|
471
|
-
for (const literal of collectStringLiterals(sourceText, filePath)) {
|
|
472
|
-
literals.add(literal);
|
|
473
|
-
if (isInternal(literal))
|
|
474
|
-
collect(join(dirname(filePath), literal));
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
collect(configFilePath);
|
|
478
|
-
for (const input of inputs) {
|
|
479
|
-
if (!input.optional && (isDeferResolve(input) || isDependency(input))) {
|
|
480
|
-
const name = getPackageNameFromSpecifier(input.specifier);
|
|
481
|
-
if (name && !literals.has(name))
|
|
482
|
-
input.optional = true;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
460
|
getConfigurationHints(type, patterns, filePaths, includedPaths, compilerExtensions) {
|
|
487
461
|
const hints = [];
|
|
488
462
|
const entries = this.config[type].filter(pattern => !pattern.startsWith('!'));
|
|
@@ -495,18 +469,24 @@ export class WorkspaceWorker {
|
|
|
495
469
|
hints.push({ type: `${type}-empty`, identifier, workspaceName });
|
|
496
470
|
return hints;
|
|
497
471
|
}
|
|
472
|
+
const dir = this.dir;
|
|
473
|
+
const relativePaths = [];
|
|
474
|
+
const hasMatch = (isMatch) => {
|
|
475
|
+
for (let i = 0; i < filePaths.length; i++) {
|
|
476
|
+
if (isMatch((relativePaths[i] ??= relative(dir, filePaths[i]))))
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
return false;
|
|
480
|
+
};
|
|
498
481
|
for (const pattern of patterns) {
|
|
499
482
|
if (pattern.startsWith('!'))
|
|
500
483
|
continue;
|
|
501
|
-
const
|
|
502
|
-
if (includedPaths.has(
|
|
484
|
+
const id = pattern.replace(/!$/, '');
|
|
485
|
+
if (includedPaths.has(join(dir, id))) {
|
|
503
486
|
hints.push({ type: `${type}-redundant`, identifier: pattern, workspaceName });
|
|
504
487
|
}
|
|
505
|
-
else {
|
|
506
|
-
|
|
507
|
-
if (!filePaths.some(filePath => matcher(filePath))) {
|
|
508
|
-
hints.push({ type: `${type}-empty`, identifier: pattern, workspaceName });
|
|
509
|
-
}
|
|
488
|
+
else if (!hasMatch(picomatch(id))) {
|
|
489
|
+
hints.push({ type: `${type}-empty`, identifier: pattern, workspaceName });
|
|
510
490
|
}
|
|
511
491
|
}
|
|
512
492
|
if (type === 'project' && compilerExtensions) {
|
|
@@ -7,6 +7,11 @@ import { walkCommands } from '../util/scripts.js';
|
|
|
7
7
|
import { getDependenciesFromCommand } from './command.js';
|
|
8
8
|
import { toScript } from './util.js';
|
|
9
9
|
const collectExpansionScripts = (word, out) => {
|
|
10
|
+
if (word.type === 'ArrayValue') {
|
|
11
|
+
for (const element of word.elements)
|
|
12
|
+
collectExpansionScripts(element, out);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
10
15
|
if (!word.parts)
|
|
11
16
|
return;
|
|
12
17
|
for (const part of word.parts) {
|
|
@@ -63,11 +68,12 @@ export const getDependenciesFromScript = (script, options) => {
|
|
|
63
68
|
const processCommand = (node, pending) => {
|
|
64
69
|
if (node.name)
|
|
65
70
|
collectExpansionScripts(node.name, pending);
|
|
66
|
-
for (const prefix of node.prefix)
|
|
67
|
-
if (prefix.
|
|
71
|
+
for (const prefix of node.prefix) {
|
|
72
|
+
if (prefix.type === 'Assignment')
|
|
68
73
|
collectExpansionScripts(prefix.value, pending);
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
}
|
|
75
|
+
for (const arg of node.args)
|
|
76
|
+
collectExpansionScripts(arg.type === 'Assignment' ? arg.value : arg, pending);
|
|
71
77
|
if (definedFunctions.size && node.name && definedFunctions.has(extractBinary(node.name.value)))
|
|
72
78
|
return [];
|
|
73
79
|
return getDependenciesFromCommand(node, { ...options, fromArgs }, fromWords);
|
|
@@ -2,4 +2,4 @@ import type { Command, Word } from 'unbash';
|
|
|
2
2
|
import type { BinaryResolverOptions, GetInputsFromScriptsOptions } from '../types/config.ts';
|
|
3
3
|
import { type Input } from '../util/input.ts';
|
|
4
4
|
export declare const getInputsFromNodeOptions: (prefix: Command['prefix']) => Input[];
|
|
5
|
-
export declare const getDependenciesFromCommand: (node: Command, options: BinaryResolverOptions, fromWords?: (words: Word[], options: GetInputsFromScriptsOptions) => Input[]) => Input[];
|
|
5
|
+
export declare const getDependenciesFromCommand: (node: Pick<Command, 'name' | 'prefix' | 'args'>, options: BinaryResolverOptions, fromWords?: (words: Word[], options: GetInputsFromScriptsOptions) => Input[]) => Input[];
|
package/dist/binaries/command.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { Plugins, pluginArgsMap } from '../plugins.js';
|
|
2
2
|
import { toDeferResolve } from '../util/input.js';
|
|
3
3
|
import { extractBinary, isInNodeModulesBin, isRelativeNodeModulesBin } from '../util/modules.js';
|
|
4
|
+
import { getCommandWords } from '../util/scripts.js';
|
|
4
5
|
import { resolve as fallbackResolve, spawningBinaries } from './fallback.js';
|
|
5
6
|
import KnownResolvers, { isPackageManager } from './resolvers/index.js';
|
|
6
7
|
import { resolve as resolverFromPlugins } from './plugins.js';
|
|
7
8
|
import { parseNodeArgs, toCommandBinary } from './util.js';
|
|
8
|
-
export const getInputsFromNodeOptions = (prefix) =>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
export const getInputsFromNodeOptions = (prefix) => {
|
|
10
|
+
const inputs = [];
|
|
11
|
+
for (const item of prefix) {
|
|
12
|
+
if (item.type !== 'Assignment' || item.name !== 'NODE_OPTIONS' || item.value.type !== 'Word')
|
|
13
|
+
continue;
|
|
14
|
+
const args = parseNodeArgs(item.value.value.split(' '));
|
|
15
|
+
if (args.require)
|
|
16
|
+
for (const id of [args.require].flat())
|
|
17
|
+
inputs.push(toDeferResolve(id));
|
|
18
|
+
}
|
|
19
|
+
return inputs;
|
|
20
|
+
};
|
|
15
21
|
export const getDependenciesFromCommand = (node, options, fromWords = options.fromArgs) => {
|
|
16
22
|
const text = node.name?.value;
|
|
17
23
|
if (!text)
|
|
@@ -31,7 +37,7 @@ export const getDependenciesFromCommand = (node, options, fromWords = options.fr
|
|
|
31
37
|
};
|
|
32
38
|
}
|
|
33
39
|
const { fromArgs } = options;
|
|
34
|
-
const words = node
|
|
40
|
+
const words = getCommandWords(node);
|
|
35
41
|
if (binary === '!' || binary === 'test')
|
|
36
42
|
return fromArgs(words);
|
|
37
43
|
const fromNodeOptions = getInputsFromNodeOptions(node.prefix);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const fencedCodeBlockMatcher = /```[\s\S]*?```/g;
|
|
2
2
|
export const inlineCodeMatcher = /`[^`]+`/g;
|
|
3
|
-
export const scriptExtractor = /<script\b((?:[^>"']|"[^"]*"|'[^']*')*)>([\s\S]*?)<\/script>/gi;
|
|
3
|
+
export const scriptExtractor = /<script\b((?:[^>"']|"[^"]*"|'[^']*')*)(?<!\/)>([\s\S]*?)<\/script>/gi;
|
|
4
4
|
export const styleExtractor = /<style\b((?:[^>"']|"[^"]*"|'[^']*')*)>([\s\S]*?)<\/style>/gi;
|
|
5
5
|
const langAttrMatcher = /\blang\s*=\s*["']([^"']+)["']/i;
|
|
6
6
|
export const blockCommentMatcher = /\/\*[\s\S]*?\*\//g;
|
package/dist/graph/build.js
CHANGED
|
@@ -9,7 +9,7 @@ import { existsSync } from 'node:fs';
|
|
|
9
9
|
import picomatch from 'picomatch';
|
|
10
10
|
import { tryRealpath } from '../util/fs.js';
|
|
11
11
|
import { createManifest } from '../util/package-json.js';
|
|
12
|
-
import { _glob, _syncGlob,
|
|
12
|
+
import { _glob, _syncGlob, prependDirToPattern as prependDir, removeProductionSuffix } from '../util/glob.js';
|
|
13
13
|
import { isAlias, isCatalog, isConfig, isDeferResolveEntry, isDeferResolveProductionEntry, isEntry, isIgnore, isProductionEntry, isProject, toDeferResolveEntry, toDependency, toProductionEntry, } from '../util/input.js';
|
|
14
14
|
import { isAmbientDeclarationFile } from '../typescript/ast-nodes.js';
|
|
15
15
|
import { resolveImportGlobs } from '../typescript/glob-imports.js';
|
|
@@ -19,6 +19,7 @@ import { createFileNode, updateImportMap } from '../util/module-graph.js';
|
|
|
19
19
|
import { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from '../util/modules.js';
|
|
20
20
|
import { perfObserver } from '../util/Performance.js';
|
|
21
21
|
import { getEntrySpecifiersFromManifest, getManifestImportDependencies } from '../util/package-json.js';
|
|
22
|
+
import { expandIgnorePatterns } from '../util/parse-and-convert-gitignores.js';
|
|
22
23
|
import { dirname, extname, isAbsolute, isInNodeModules, isInternal, join, relative } from '../util/path.js';
|
|
23
24
|
import { extensionAlias } from '../util/resolve.js';
|
|
24
25
|
import { augmentWorkspace, getToSourcePathsHandler, toSourceMappedSpecifiers } from '../util/to-source-path.js';
|
|
@@ -50,8 +51,8 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
deputy.setWorkspacePkgNames(chief.availableWorkspacePkgNames);
|
|
53
|
-
collector.addIgnorePatterns(chief.config.ignore.map(id => ({ pattern:
|
|
54
|
-
collector.addIgnoreFilesPatterns(chief.config.ignoreFiles.map(id => ({ pattern:
|
|
54
|
+
collector.addIgnorePatterns(chief.config.ignore.map(id => ({ pattern: id, id })));
|
|
55
|
+
collector.addIgnoreFilesPatterns(chief.config.ignoreFiles.map(id => ({ pattern: id, id })));
|
|
55
56
|
if (options.configFilePath) {
|
|
56
57
|
principal.addEntryPath(options.configFilePath, { skipExportsAnalysis: true });
|
|
57
58
|
}
|
|
@@ -78,7 +79,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
78
79
|
continue;
|
|
79
80
|
const dependencies = deputy.getDependencies(name);
|
|
80
81
|
const tsConfigFilePath = join(dir, options.tsConfigFile ?? 'tsconfig.json');
|
|
81
|
-
const { isFile, compilerOptions, fileNames, include, exclude, sourceMapPairs, paths: tsConfigPaths, } = await loadTSConfig(tsConfigFilePath);
|
|
82
|
+
const { isFile, compilerOptions, fileNames, include, exclude, sourceMapPairs, paths: tsConfigPaths, } = await loadTSConfig(tsConfigFilePath, options.isUseTscFiles);
|
|
82
83
|
const [definitionPaths, tscSourcePaths] = partition(fileNames, filePath => IS_DTS.test(filePath));
|
|
83
84
|
const worker = new WorkspaceWorker({
|
|
84
85
|
name,
|
|
@@ -121,7 +122,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
121
122
|
inputs.add(toProductionEntry(tryRealpath(id), { containingFilePath: tsConfigFilePath }));
|
|
122
123
|
}
|
|
123
124
|
const sharedGlobOptions = { cwd: options.cwd, dir, gitignore: options.gitignore };
|
|
124
|
-
const fn = (id) => ({ pattern: prependDir(
|
|
125
|
+
const fn = (id) => ({ pattern: prependDir(name, id), id, workspaceName: name });
|
|
125
126
|
collector.addIgnorePatterns(config.ignore.map(fn));
|
|
126
127
|
collector.addIgnoreFilesPatterns(config.ignoreFiles.map(fn));
|
|
127
128
|
const entrySpecifiersFromManifest = getEntrySpecifiersFromManifest(manifest);
|
|
@@ -246,16 +247,30 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
}
|
|
249
|
-
const
|
|
250
|
+
const developmentMatchers = [];
|
|
250
251
|
if (options.isProduction) {
|
|
252
|
+
const toPattern = (pattern) => {
|
|
253
|
+
const id = removeProductionSuffix(pattern);
|
|
254
|
+
return isAbsolute(id) ? relative(options.cwd, id) : join(name, id);
|
|
255
|
+
};
|
|
251
256
|
for (const map of [entryPatterns, entryPatternsSkipExports]) {
|
|
252
|
-
for (const patterns of map.values())
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
for (const patterns of map.values()) {
|
|
258
|
+
const [excluded, included] = partition(patterns, pattern => pattern.startsWith('!'));
|
|
259
|
+
if (included.length === 0)
|
|
260
|
+
continue;
|
|
261
|
+
const ignore = expandIgnorePatterns(excluded.map(pattern => removeProductionSuffix(pattern.slice(1))));
|
|
262
|
+
developmentMatchers.push(picomatch(included.map(toPattern), { ignore: ignore.map(toPattern), dot: true }));
|
|
263
|
+
}
|
|
255
264
|
}
|
|
256
265
|
}
|
|
266
|
+
const productionPaths = (paths) => developmentMatchers.length
|
|
267
|
+
? paths.filter(path => {
|
|
268
|
+
const relativePath = relative(options.cwd, path);
|
|
269
|
+
return !developmentMatchers.some(match => match(relativePath));
|
|
270
|
+
})
|
|
271
|
+
: paths;
|
|
257
272
|
const userEntryPatterns = options.isProduction
|
|
258
|
-
? worker.getProductionEntryFilePatterns(
|
|
273
|
+
? worker.getProductionEntryFilePatterns()
|
|
259
274
|
: worker.getEntryFilePatterns();
|
|
260
275
|
const userEntryPaths = await _glob({
|
|
261
276
|
...sharedGlobOptions,
|
|
@@ -272,7 +287,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
272
287
|
]);
|
|
273
288
|
const label = `entry paths from plugins${group !== DEFAULT_GROUP ? ` - ${group}` : ''}`;
|
|
274
289
|
const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });
|
|
275
|
-
principal.addEntryPaths(pluginWorkspaceEntryPaths);
|
|
290
|
+
principal.addEntryPaths(productionPaths(pluginWorkspaceEntryPaths));
|
|
276
291
|
}
|
|
277
292
|
{
|
|
278
293
|
const patterns = worker.getPluginEntryFilePatterns([
|
|
@@ -293,7 +308,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
293
308
|
collector.addConfigurationHint(hint);
|
|
294
309
|
}
|
|
295
310
|
const hasExplicitEntries = config.entry.some(pattern => !isDefaultPattern('entry', pattern));
|
|
296
|
-
principal.addEntryPaths(userEntryPaths, hasExplicitEntries ? { skipExportsAnalysis: false } : undefined);
|
|
311
|
+
principal.addEntryPaths(productionPaths(userEntryPaths), hasExplicitEntries ? { skipExportsAnalysis: false } : undefined);
|
|
297
312
|
if (options.isUseTscFiles && isFile) {
|
|
298
313
|
const isIgnoredWorkspace = chief.createIgnoredWorkspaceMatcher(name, dir);
|
|
299
314
|
debugLogArray(name, 'Using tsconfig files as project files', tscSourcePaths);
|
|
@@ -306,10 +321,11 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
306
321
|
}
|
|
307
322
|
if (extensions.length > 0) {
|
|
308
323
|
const extPart = extensions.length === 1 ? extensions[0] : `.{${extensions.map(ext => ext.slice(1)).join(',')}}`;
|
|
309
|
-
const
|
|
324
|
+
const tsConfigDir = relative(dir, dirname(tsConfigFilePath));
|
|
325
|
+
const bases = include ? new Set(include.map(p => join(tsConfigDir, picomatch.scan(p).base))) : new Set(['']);
|
|
310
326
|
const patterns = [
|
|
311
|
-
...Array.from(bases, base =>
|
|
312
|
-
...(exclude?.map(p => `!${p}`) ?? []),
|
|
327
|
+
...Array.from(bases, base => join(base, `**/*${extPart}`)),
|
|
328
|
+
...(exclude?.map(p => `!${join(tsConfigDir, p)}`) ?? []),
|
|
313
329
|
];
|
|
314
330
|
const compilerPaths = await _glob({ ...sharedGlobOptions, patterns, label: 'compiler extension paths' });
|
|
315
331
|
for (const compilerPath of compilerPaths) {
|
|
@@ -320,7 +336,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
320
336
|
}
|
|
321
337
|
else {
|
|
322
338
|
const patterns = options.isProduction
|
|
323
|
-
? worker.getProductionProjectFilePatterns(
|
|
339
|
+
? worker.getProductionProjectFilePatterns()
|
|
324
340
|
: worker.getProjectFilePatterns([
|
|
325
341
|
...(productionPatternsSkipExports.get(DEFAULT_GROUP) ?? []),
|
|
326
342
|
...projectFilePatterns,
|
|
@@ -332,7 +348,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
|
|
|
332
348
|
for (const hint of hints)
|
|
333
349
|
collector.addConfigurationHint(hint);
|
|
334
350
|
}
|
|
335
|
-
for (const projectPath of projectPaths)
|
|
351
|
+
for (const projectPath of productionPaths(projectPaths))
|
|
336
352
|
principal.addProjectPath(projectPath);
|
|
337
353
|
}
|
|
338
354
|
worker.onDispose();
|
package/dist/manifest/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ declare const getMetaDataFromPackageJson: ({ dir, packageNames }: Options) => {
|
|
|
7
7
|
hostDependencies: HostDependencies;
|
|
8
8
|
installedBinaries: InstalledBinaries;
|
|
9
9
|
hasTypesIncluded: Set<string>;
|
|
10
|
+
uninstalledDependencies: Set<string>;
|
|
10
11
|
};
|
|
11
12
|
export declare const getDependencyMetaData: typeof getMetaDataFromPackageJson;
|
|
12
13
|
export {};
|