@@ -1080,6 +1080,7 @@ import {
10801080 tryGetJSDocSatisfiesTypeNode,
10811081 tryGetModuleSpecifierFromDeclaration,
10821082 tryGetPropertyAccessOrIdentifierToString,
1083+ tryGetTextOfPropertyName,
10831084 TryStatement,
10841085 TupleType,
10851086 TupleTypeNode,
@@ -4706,7 +4707,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
47064707 }
47074708
47084709 function getImportAttributesFromLocation(location: Node): ImportAttributes | undefined {
4709- // Try to find import attributes from the location node
47104710 const importDecl = findAncestor(location, isImportDeclaration);
47114711 if (importDecl?.attributes) {
47124712 return importDecl.attributes;
@@ -4719,17 +4719,55 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
47194719 if (importType?.attributes) {
47204720 return importType.attributes;
47214721 }
4722+ const importCall = findAncestor(location, isImportCall);
4723+ if (importCall) {
4724+ return getImportAttributesFromImportCall(importCall);
4725+ }
4726+ return undefined;
4727+ }
4728+
4729+ function getImportAttributesFromImportCall(importCall: ImportCall): ImportAttributes | undefined {
4730+ // import("./module", { with: { type: "json" } })
4731+ if (importCall.arguments.length < 2) {
4732+ return undefined;
4733+ }
4734+ const optionsArg = importCall.arguments[1];
4735+ if (!isObjectLiteralExpression(optionsArg)) {
4736+ return undefined;
4737+ }
4738+ for (const prop of optionsArg.properties) {
4739+ if (isPropertyAssignment(prop) && isObjectLiteralExpression(prop.initializer)) {
4740+ const propName = tryGetTextOfPropertyName(prop.name);
4741+ if (propName === "with" as __String || propName === "assert" as __String) {
4742+ return createSyntheticImportAttributes(prop.initializer);
4743+ }
4744+ }
4745+ }
47224746 return undefined;
47234747 }
47244748
4749+ function createSyntheticImportAttributes(objectLiteral: ObjectLiteralExpression): ImportAttributes | undefined {
4750+ // Create synthetic ImportAttribute elements from PropertyAssignment nodes
4751+ // PropertyAssignment has { name, initializer } but ImportAttribute needs { name, value }
4752+ const elements: { name: PropertyName; value: Expression; }[] = [];
4753+ for (const prop of objectLiteral.properties) {
4754+ if (isPropertyAssignment(prop) && isStringLiteral(prop.initializer)) {
4755+ elements.push({ name: prop.name, value: prop.initializer });
4756+ }
4757+ }
4758+ if (elements.length === 0) {
4759+ return undefined;
4760+ }
4761+ return { elements: elements as unknown as NodeArray<ImportAttribute> } as unknown as ImportAttributes;
4762+ }
4763+
47254764 function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node | undefined, isForAugmentation = false): Symbol | undefined {
47264765 if (errorNode && startsWith(moduleReference, "@types/")) {
47274766 const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1;
47284767 const withoutAtTypePrefix = removePrefix(moduleReference, "@types/");
47294768 error(errorNode, diag, withoutAtTypePrefix, moduleReference);
47304769 }
47314770
4732- // Get import attributes from the import/export statement
47334771 const importAttributes = getImportAttributesFromLocation(location);
47344772
47354773 const ambientModule = tryFindAmbientModule(moduleReference, /*withAugmentations*/ true, importAttributes);
@@ -4820,6 +4858,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
48204858 }
48214859
48224860 if (sourceFile.symbol) {
4861+ // Check for pattern ambient module with matching import attributes
4862+ if (importAttributes?.elements.length) {
4863+ const patternMatch = tryFindPatternAmbientModuleWithAttributes(moduleReference, importAttributes);
4864+ if (patternMatch) {
4865+ return patternMatch;
4866+ }
4867+ }
4868+
48234869 if (errorNode && resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) {
48244870 errorOnImplicitAnyModule(/*isError*/ false, errorNode, currentSourceFile, mode, resolvedModule, moduleReference);
48254871 }
@@ -4865,32 +4911,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
48654911 if (patternAmbientModules) {
48664912 const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference);
48674913 if (pattern) {
4868- // Check if the pattern module has matching import attributes
4869- const patternSymbol = pattern.symbol;
4870- if (patternSymbol.declarations) {
4871- const hasMatchingDeclaration = patternSymbol.declarations.some(decl => {
4872- if (isModuleDeclaration(decl) && isStringLiteral(decl.name)) {
4873- return importAttributesMatch(importAttributes, decl.withClause);
4874- }
4875- return false;
4876- });
4877- if (!hasMatchingDeclaration) {
4878- // Pattern matched but attributes don't match
4879- if (errorNode && moduleNotFoundError) {
4880- error(errorNode, Diagnostics.No_ambient_module_declaration_matches_import_of_0_with_the_specified_import_attributes, moduleReference);
4881- }
4882- return undefined;
4914+ if (!hasMatchingImportAttributes(pattern.symbol, importAttributes)) {
4915+ if (errorNode && moduleNotFoundError) {
4916+ error(errorNode, Diagnostics.No_ambient_module_declaration_matches_import_of_0_with_the_specified_import_attributes, moduleReference);
48834917 }
4918+ return undefined;
48844919 }
4885- // If the module reference matched a pattern ambient module ('*.foo') but there's also a
4886- // module augmentation by the specific name requested ('a.foo'), we store the merged symbol
4887- // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports
4888- // from a.foo.
4889- const augmentation = patternAmbientModuleAugmentations && patternAmbientModuleAugmentations.get(moduleReference);
4890- if (augmentation) {
4891- return getMergedSymbol(augmentation);
4892- }
4893- return getMergedSymbol(pattern.symbol);
4920+ const augmentation = patternAmbientModuleAugmentations?.get(moduleReference);
4921+ return getMergedSymbol(augmentation || pattern.symbol);
48944922 }
48954923 }
48964924
@@ -16048,20 +16076,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1604816076 }
1604916077
1605016078 function importAttributesMatch(importAttributes: ImportAttributes | undefined, moduleAttributes: ImportAttributes | undefined): boolean {
16051- // If neither has attributes, they match
1605216079 if (!importAttributes && !moduleAttributes) {
1605316080 return true;
1605416081 }
16055- // If only one has attributes, they don't match
1605616082 if (!importAttributes || !moduleAttributes) {
1605716083 return false;
1605816084 }
16059- // Both have attributes - check if they match
16060- // For now, we require exact match of all attributes
1606116085 if (importAttributes.elements.length !== moduleAttributes.elements.length) {
1606216086 return false;
1606316087 }
16064- // Create a map of module attributes for easier lookup
1606516088 const moduleAttrsMap = new Map<string, string>();
1606616089 for (const attr of moduleAttributes.elements) {
1606716090 const name = isIdentifier(attr.name) ? idText(attr.name) : attr.name.text;
@@ -16070,7 +16093,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1607016093 moduleAttrsMap.set(name, value);
1607116094 }
1607216095 }
16073- // Check that all import attributes match
1607416096 for (const attr of importAttributes.elements) {
1607516097 const name = isIdentifier(attr.name) ? idText(attr.name) : attr.name.text;
1607616098 const value = isStringLiteral(attr.value) ? attr.value.text : undefined;
@@ -16081,29 +16103,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1608116103 return true;
1608216104 }
1608316105
16106+ function hasMatchingImportAttributes(symbol: Symbol, importAttributes: ImportAttributes | undefined): boolean {
16107+ return !symbol.declarations || symbol.declarations.some(decl => isModuleDeclaration(decl) && isStringLiteral(decl.name) && importAttributesMatch(importAttributes, decl.withClause));
16108+ }
16109+
1608416110 function tryFindAmbientModule(moduleName: string, withAugmentations: boolean, importAttributes?: ImportAttributes) {
1608516111 if (isExternalModuleNameRelative(moduleName)) {
1608616112 return undefined;
1608716113 }
1608816114 const symbol = getSymbol(globals, '"' + moduleName + '"' as __String, SymbolFlags.ValueModule);
16089- if (!symbol) {
16115+ if (!symbol || !hasMatchingImportAttributes(symbol, importAttributes) ) {
1609016116 return undefined;
1609116117 }
16092- // Check if the module declaration has matching import attributes
16093- const declarations = symbol.declarations;
16094- if (declarations) {
16095- const hasMatchingDeclaration = declarations.some(decl => {
16096- if (isModuleDeclaration(decl) && isStringLiteral(decl.name)) {
16097- return importAttributesMatch(importAttributes, decl.withClause);
16098- }
16099- return false;
16100- });
16101- if (!hasMatchingDeclaration) {
16102- return undefined;
16103- }
16118+ return withAugmentations ? getMergedSymbol(symbol) : symbol;
16119+ }
16120+
16121+ function tryFindPatternAmbientModuleWithAttributes(moduleReference: string, importAttributes: ImportAttributes | undefined): Symbol | undefined {
16122+ if (!patternAmbientModules) {
16123+ return undefined;
16124+ }
16125+ const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference);
16126+ if (!pattern || !hasMatchingImportAttributes(pattern.symbol, importAttributes)) {
16127+ return undefined;
1610416128 }
16105- // merged symbol is module declaration symbol combined with all augmentations
16106- return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol;
16129+ const augmentation = patternAmbientModuleAugmentations?.get(moduleReference);
16130+ return getMergedSymbol(augmentation || pattern. symbol) ;
1610716131 }
1610816132
1610916133 function hasEffectiveQuestionToken(node: ParameterDeclaration | JSDocParameterTag | JSDocPropertyTag) {
0 commit comments