Skip to content

Commit 8f3fbdb

Browse files
committed
Experiment: use more emit info in interop checking
1 parent 3c504d8 commit 8f3fbdb

File tree

72 files changed

+288
-352
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+288
-352
lines changed

src/compiler/checker.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -4058,22 +4058,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
40584058
|| isNamespaceExport(node));
40594059
}
40604060

4061-
function getUsageModeForExpression(usage: Expression) {
4062-
return isStringLiteralLike(usage) ? host.getModeForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
4061+
function getEmitSyntaxForModuleSpecifierExpression(usage: Expression) {
4062+
return isStringLiteralLike(usage) ? host.getEmitSyntaxForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
40634063
}
40644064

40654065
function isESMFormatImportImportingCommonjsFormatFile(usageMode: ResolutionMode, targetMode: ResolutionMode) {
40664066
return usageMode === ModuleKind.ESNext && targetMode === ModuleKind.CommonJS;
40674067
}
40684068

40694069
function isOnlyImportedAsDefault(usage: Expression) {
4070-
const usageMode = getUsageModeForExpression(usage);
4070+
const usageMode = getEmitSyntaxForModuleSpecifierExpression(usage);
40714071
return usageMode === ModuleKind.ESNext && endsWith((usage as StringLiteralLike).text, Extension.Json);
40724072
}
40734073

40744074
function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean, usage: Expression) {
4075-
const usageMode = file && getUsageModeForExpression(usage);
4076-
if (file && usageMode !== undefined && ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) {
4075+
const usageMode = file && getEmitSyntaxForModuleSpecifierExpression(usage);
4076+
if (file && usageMode !== undefined) {
40774077
const result = isESMFormatImportImportingCommonjsFormatFile(usageMode, impliedNodeFormatForInteropChecking(file, compilerOptions));
40784078
if (usageMode === ModuleKind.ESNext || result) {
40794079
return result;
@@ -5301,7 +5301,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
53015301
}
53025302

53035303
const targetFile = moduleSymbol?.declarations?.find(isSourceFile);
5304-
const isEsmCjsRef = targetFile && isESMFormatImportImportingCommonjsFormatFile(getUsageModeForExpression(reference), impliedNodeFormatForInteropChecking(targetFile, compilerOptions));
5304+
const isEsmCjsRef = targetFile && isESMFormatImportImportingCommonjsFormatFile(getEmitSyntaxForModuleSpecifierExpression(reference), impliedNodeFormatForInteropChecking(targetFile, compilerOptions));
53055305
if (getESModuleInterop(compilerOptions) || isEsmCjsRef) {
53065306
let sigs = getSignaturesOfStructuredType(type, SignatureKind.Call);
53075307
if (!sigs || !sigs.length) {
@@ -46071,7 +46071,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4607146071
return; // Other grammar checks do not apply to type-only imports with resolution mode assertions
4607246072
}
4607346073

46074-
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
46074+
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getEmitSyntaxForModuleSpecifierExpression(declaration.moduleSpecifier);
4607546075
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.Preserve) {
4607646076
const message = isImportAttributes
4607746077
? moduleKind === ModuleKind.NodeNext

src/compiler/program.ts

+40-9
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ import {
136136
getPositionOfLineAndCharacter,
137137
getPropertyArrayElementValue,
138138
getResolveJsonModule,
139+
getResolvePackageJsonExports,
140+
getResolvePackageJsonImports,
139141
getRootLength,
140142
getSetExternalModuleIndicator,
141143
getSourceFileOfNode,
@@ -913,23 +915,46 @@ function getModeForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMod
913915
return override;
914916
}
915917
}
918+
919+
const emitSyntax = getEmitSyntaxForUsageLocationWorker(file, usage, compilerOptions);
920+
const moduleResolution = compilerOptions && getEmitModuleResolutionKind(compilerOptions);
921+
if (
922+
compilerOptions && (
923+
getResolvePackageJsonExports(compilerOptions) ||
924+
getResolvePackageJsonImports(compilerOptions) ||
925+
ModuleResolutionKind.Node16 <= moduleResolution! && moduleResolution! <= ModuleResolutionKind.NodeNext
926+
)
927+
) {
928+
return emitSyntax;
929+
}
930+
}
931+
932+
function getEmitSyntaxForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMode; }, usage: StringLiteralLike, compilerOptions?: CompilerOptions): ResolutionMode {
916933
if (compilerOptions && getEmitModuleKind(compilerOptions) === ModuleKind.Preserve) {
917934
return (usage.parent.parent && isImportEqualsDeclaration(usage.parent.parent) || isRequireCall(usage.parent, /*requireStringLiteralLikeArgument*/ false))
918935
? ModuleKind.CommonJS
919936
: ModuleKind.ESNext;
920937
}
921-
if (file.impliedNodeFormat === undefined || !(compilerOptions && impliedNodeFormatAffectsModuleResolution(compilerOptions))) {
922-
// TODO: we don't know whether this is being called for module resolution, emit, or interop checking
938+
if (!compilerOptions) {
939+
// This should always be provided, but we try to fail somewhat
940+
// gracefully to allow projects like ts-node time to update.
923941
return undefined;
924942
}
925-
if (file.impliedNodeFormat !== ModuleKind.ESNext) {
926-
// in cjs files, import call expressions are esm format, otherwise everything is cjs
927-
return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
943+
if (impliedNodeFormatAffectsModuleResolution(compilerOptions)) {
944+
if (file.impliedNodeFormat !== ModuleKind.ESNext) {
945+
// in cjs files, import call expressions are esm format, otherwise everything is cjs
946+
return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
947+
}
948+
const exprParentParent = walkUpParenthesizedExpressions(usage.parent)?.parent;
949+
return exprParentParent && isImportEqualsDeclaration(exprParentParent) ? ModuleKind.CommonJS : ModuleKind.ESNext;
928950
}
929-
// in esm files, import=require statements are cjs format, otherwise everything is esm
930-
// imports are only parent'd up to their containing declaration/expression, so access farther parents with care
931-
const exprParentParent = walkUpParenthesizedExpressions(usage.parent)?.parent;
932-
return exprParentParent && isImportEqualsDeclaration(exprParentParent) ? ModuleKind.CommonJS : ModuleKind.ESNext;
951+
if (getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS) {
952+
return ModuleKind.CommonJS;
953+
}
954+
if (emitModuleKindIsNonNodeESM(getEmitModuleKind(compilerOptions))) {
955+
return ModuleKind.ESNext;
956+
}
957+
return undefined;
933958
}
934959

935960
/** @internal */
@@ -1897,6 +1922,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18971922
isSourceFileFromExternalLibrary,
18981923
isSourceFileDefaultLibrary,
18991924
getModeForUsageLocation,
1925+
getEmitSyntaxForUsageLocation,
19001926
getModeForResolutionAtIndex,
19011927
getSourceFileFromReference,
19021928
getLibFileFromReference,
@@ -4931,6 +4957,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
49314957
return getModeForUsageLocationWorker(file, usage, optionsForFile);
49324958
}
49334959

4960+
function getEmitSyntaxForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode {
4961+
const optionsForFile = getRedirectReferenceForResolution(file)?.commandLine.options || options;
4962+
return getEmitSyntaxForUsageLocationWorker(file, usage, optionsForFile);
4963+
}
4964+
49344965
function getModeForResolutionAtIndex(file: SourceFile, index: number): ResolutionMode {
49354966
return getModeForUsageLocation(file, getModuleNameStringLiteralAt(file, index));
49364967
}

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4847,6 +4847,7 @@ export interface TypeCheckerHost extends ModuleSpecifierResolutionHost {
48474847
getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>;
48484848
getProjectReferenceRedirect(fileName: string): string | undefined;
48494849
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
4850+
getEmitSyntaxForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
48504851
getModeForUsageLocation(file: SourceFile, usage: StringLiteralLike): ResolutionMode;
48514852

48524853
getResolvedModule(f: SourceFile, moduleName: string, mode: ResolutionMode): ResolvedModuleWithFailedLookupLocations | undefined;

src/compiler/utilities.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -8611,9 +8611,8 @@ export function impliedNodeFormatAffectsModuleResolution(options: CompilerOption
86118611
}
86128612

86138613
/** @internal */
8614-
export function impliedNodeFormatAffectsInteropChecking(options: CompilerOptions) {
8615-
const moduleKind = getEmitModuleKind(options);
8616-
return ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext;
8614+
export function impliedNodeFormatAffectsInteropChecking(_options: CompilerOptions) {
8615+
return true;
86178616
}
86188617

86198618
/** @internal */

tests/baselines/reference/api/typescript.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9875,7 +9875,7 @@ declare namespace ts {
98759875
},
98769876
usage: StringLiteralLike,
98779877
compilerOptions: CompilerOptions,
9878-
): ModuleKind.CommonJS | ModuleKind.ESNext | undefined;
9878+
): ResolutionMode;
98799879
function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[];
98809880
/**
98819881
* A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the

tests/baselines/reference/bundlerDirectoryModule(moduleresolution=bundler).trace.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,8 @@
937937
"Directory '/.src/node_modules' does not exist, skipping all lookups in it.",
938938
"Directory '/node_modules' does not exist, skipping all lookups in it.",
939939
"======== Module name '@typescript/lib-es2023/collection' was not resolved. ========",
940-
<<<<<<< HEAD
940+
"File '/.ts/package.json' does not exist according to earlier cached lookups.",
941+
"File '/package.json' does not exist according to earlier cached lookups.",
941942
"======== Resolving module '@typescript/lib-es2023/intl' from '/.src/__lib_node_modules_lookup_lib.es2023.intl.d.ts__.ts'. ========",
942943
"Explicitly specified module resolution kind: 'Node10'.",
943944
"Loading module '@typescript/lib-es2023/intl' from 'node_modules' folder, target file types: TypeScript, Declaration.",
@@ -951,10 +952,8 @@
951952
"Directory '/.src/node_modules' does not exist, skipping all lookups in it.",
952953
"Directory '/node_modules' does not exist, skipping all lookups in it.",
953954
"======== Module name '@typescript/lib-es2023/intl' was not resolved. ========",
954-
=======
955955
"File '/.ts/package.json' does not exist according to earlier cached lookups.",
956956
"File '/package.json' does not exist according to earlier cached lookups.",
957-
>>>>>>> a3b9541d66 (Update trace baselines)
958957
"======== Resolving module '@typescript/lib-esnext/intl' from '/.src/__lib_node_modules_lookup_lib.esnext.intl.d.ts__.ts'. ========",
959958
"Explicitly specified module resolution kind: 'Node10'.",
960959
"Loading module '@typescript/lib-esnext/intl' from 'node_modules' folder, target file types: TypeScript, Declaration.",

tests/baselines/reference/bundlerRelative1(module=esnext).types

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { y } from "./redirect";
3838
import {} from "./redirect/index";
3939

4040
import a from "./types/esm";
41-
>a : string
41+
>a : typeof a
4242

4343
import * as esm from "./types/esm";
4444
>esm : typeof esm
@@ -47,5 +47,5 @@ import b from "./types/cjs";
4747
>b : string
4848

4949
import * as cjs from "./types/cjs";
50-
>cjs : string
50+
>cjs : { default: string; }
5151

tests/baselines/reference/bundlerRelative1(module=preserve).types

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { y } from "./redirect";
3838
import {} from "./redirect/index";
3939

4040
import a from "./types/esm";
41-
>a : string
41+
>a : typeof a
4242

4343
import * as esm from "./types/esm";
4444
>esm : typeof esm
@@ -47,5 +47,5 @@ import b from "./types/cjs";
4747
>b : string
4848

4949
import * as cjs from "./types/cjs";
50-
>cjs : string
50+
>cjs : { default: string; }
5151

tests/baselines/reference/bundlerSyntaxRestrictions(module=esnext).types

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ declare module "path" {
3333
=== /mainJs.js ===
3434
import {} from "./a";
3535
import("./a");
36-
>import("./a") : Promise<typeof _>
36+
>import("./a") : Promise<{ default: typeof _; a: "a"; }>
3737
>"./a" : "./a"
3838

3939
const _ = require("./a");

tests/baselines/reference/bundlerSyntaxRestrictions(module=preserve).types

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ declare module "path" {
3333
=== /mainJs.js ===
3434
import {} from "./a";
3535
import("./a");
36-
>import("./a") : Promise<typeof _>
36+
>import("./a") : Promise<{ default: typeof _; a: "a"; }>
3737
>"./a" : "./a"
3838

3939
const _ = require("./a");

tests/baselines/reference/customConditions(resolvepackagejsonexports=true).trace.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"File '/package.json' does not exist.",
66
"======== Resolving module 'lodash' from '/index.ts'. ========",
77
"Explicitly specified module resolution kind: 'Bundler'.",
8-
"Resolving in CJS mode with conditions 'import', 'types', 'webpack', ' browser'.",
8+
"Resolving in CJS mode with conditions 'require', 'types', 'webpack', ' browser'.",
99
"File '/package.json' does not exist according to earlier cached lookups.",
1010
"Loading module 'lodash' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
1111
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",

tests/baselines/reference/es6ExportAssignment2.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ export = a; // Error: export = not allowed in ES6
1010

1111
=== b.ts ===
1212
import * as a from "a";
13-
>a : number
13+
>a : { default: number; }
1414

tests/baselines/reference/es6ExportAssignment3.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ export = a; // OK, in ambient context
99

1010
=== b.ts ===
1111
import * as a from "a";
12-
>a : number
12+
>a : { default: number; }
1313

tests/baselines/reference/es6ImportDefaultBinding.types

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ export default a;
1010

1111
=== es6ImportDefaultBinding_1.ts ===
1212
import defaultBinding from "es6ImportDefaultBinding_0";
13-
>defaultBinding : number
13+
>defaultBinding : typeof defaultBinding
1414

1515
var x = defaultBinding;
16-
>x : number
17-
>defaultBinding : number
16+
>x : typeof defaultBinding
17+
>defaultBinding : typeof defaultBinding
1818

1919
import defaultBinding2 from "es6ImportDefaultBinding_0"; // elide this import since defaultBinding2 is not used
20-
>defaultBinding2 : number
20+
>defaultBinding2 : typeof defaultBinding
2121

Original file line numberDiff line numberDiff line change
@@ -1,38 +1,56 @@
1+
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(2,5): error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
12
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(3,27): error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'a'. Did you mean to use 'import a from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
3+
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(4,5): error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
24
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(5,27): error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'a'. Did you mean to use 'import a from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
5+
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(6,5): error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
36
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(7,27): error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'x'. Did you mean to use 'import x from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
47
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(7,30): error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'a'. Did you mean to use 'import a from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
8+
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(8,5): error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
59
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(9,27): error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'x'. Did you mean to use 'import x from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
10+
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(10,5): error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
611
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(11,27): error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'm'. Did you mean to use 'import m from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
12+
es6ImportDefaultBindingFollowedWithNamedImport1_1.ts(12,5): error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
713

814

915
==== es6ImportDefaultBindingFollowedWithNamedImport1_0.ts (0 errors) ====
1016
var a = 10;
1117
export default a;
1218

13-
==== es6ImportDefaultBindingFollowedWithNamedImport1_1.ts (6 errors) ====
19+
==== es6ImportDefaultBindingFollowedWithNamedImport1_1.ts (12 errors) ====
1420
import defaultBinding1, { } from "es6ImportDefaultBindingFollowedWithNamedImport1_0";
1521
var x1: number = defaultBinding1;
22+
~~
23+
!!! error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
1624
import defaultBinding2, { a } from "es6ImportDefaultBindingFollowedWithNamedImport1_0";
1725
~
1826
!!! error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'a'. Did you mean to use 'import a from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
1927
var x1: number = defaultBinding2;
28+
~~
29+
!!! error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
2030
import defaultBinding3, { a as b } from "es6ImportDefaultBindingFollowedWithNamedImport1_0";
2131
~
2232
!!! error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'a'. Did you mean to use 'import a from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
2333
var x1: number = defaultBinding3;
34+
~~
35+
!!! error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
2436
import defaultBinding4, { x, a as y } from "es6ImportDefaultBindingFollowedWithNamedImport1_0";
2537
~
2638
!!! error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'x'. Did you mean to use 'import x from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
2739
~
2840
!!! error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'a'. Did you mean to use 'import a from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
2941
var x1: number = defaultBinding4;
42+
~~
43+
!!! error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
3044
import defaultBinding5, { x as z, } from "es6ImportDefaultBindingFollowedWithNamedImport1_0";
3145
~
3246
!!! error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'x'. Did you mean to use 'import x from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
3347
var x1: number = defaultBinding5;
48+
~~
49+
!!! error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
3450
import defaultBinding6, { m, } from "es6ImportDefaultBindingFollowedWithNamedImport1_0";
3551
~
3652
!!! error TS2614: Module '"es6ImportDefaultBindingFollowedWithNamedImport1_0"' has no exported member 'm'. Did you mean to use 'import m from "es6ImportDefaultBindingFollowedWithNamedImport1_0"' instead?
3753
var x1: number = defaultBinding6;
54+
~~
55+
!!! error TS2322: Type 'typeof import("es6ImportDefaultBindingFollowedWithNamedImport1_0")' is not assignable to type 'number'.
3856

0 commit comments

Comments
 (0)