diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index b177c0c9e044b..ce908e232bee1 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -299,7 +299,7 @@ namespace ts {
             resolveName(name, location, meaning, excludeGlobals) {
                 return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals);
             },
-            getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
+            getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)),
             getAccessibleSymbolChain,
             getTypePredicateOfSignature,
             resolveExternalModuleSymbol,
@@ -765,7 +765,23 @@ namespace ts {
             }
         }
 
-        function getJsxNamespace(): __String {
+        function getJsxNamespace(location: Node | undefined): __String {
+            if (location) {
+                const file = getSourceFileOfNode(location);
+                if (file) {
+                    if (file.localJsxNamespace) {
+                        return file.localJsxNamespace;
+                    }
+                    const jsxPragma = file.pragmas.get("jsx");
+                    if (jsxPragma) {
+                        const chosenpragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
+                        file.localJsxFactory = parseIsolatedEntityName(chosenpragma.arguments.factory, languageVersion);
+                        if (file.localJsxFactory) {
+                            return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
+                        }
+                    }
+                }
+            }
             if (!_jsxNamespace) {
                 _jsxNamespace = "React" as __String;
                 if (compilerOptions.jsxFactory) {
@@ -15082,8 +15098,10 @@ namespace ts {
         function checkJsxFragment(node: JsxFragment, checkMode: CheckMode): Type {
             checkJsxOpeningLikeElementOrOpeningFragment(node.openingFragment, checkMode);
 
-            if (compilerOptions.jsx === JsxEmit.React && compilerOptions.jsxFactory) {
-                error(node, Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory);
+            if (compilerOptions.jsx === JsxEmit.React && (compilerOptions.jsxFactory || getSourceFileOfNode(node).pragmas.has("jsx"))) {
+                error(node, compilerOptions.jsxFactory
+                    ? Diagnostics.JSX_fragment_is_not_supported_when_using_jsxFactory
+                    : Diagnostics.JSX_fragment_is_not_supported_when_using_an_inline_JSX_factory_pragma);
             }
 
             return getJsxGlobalElementType() || anyType;
@@ -15709,7 +15727,7 @@ namespace ts {
             // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
             // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
             const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
-            const reactNamespace = getJsxNamespace();
+            const reactNamespace = getJsxNamespace(node);
             const reactLocation = isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>node).tagName : node;
             const reactSym = resolveName(reactLocation, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
             if (reactSym) {
@@ -25556,7 +25574,7 @@ namespace ts {
                     return !!(symbol && getCheckFlags(symbol) & CheckFlags.Late);
                 },
                 writeLiteralConstValue,
-                getJsxFactoryEntity: () => _jsxFactoryEntity
+                getJsxFactoryEntity: location => location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity
             };
 
             // defined here to avoid outer scope pollution
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index f120e81283a76..2e4430acf7ac6 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -3788,6 +3788,10 @@
         "category": "Error",
         "code": 17016
     },
+    "JSX fragment is not supported when using an inline JSX factory pragma": {
+        "category": "Error",
+        "code": 17017
+    },
 
     "Circularity detected while resolving configuration: {0}": {
         "category": "Error",
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index 92d353aeb0efa..bb00a967336ea 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -2375,6 +2375,9 @@ namespace ts {
             if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames;
             if (node.imports !== undefined) updated.imports = node.imports;
             if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations;
+            if (node.pragmas !== undefined) updated.pragmas = node.pragmas;
+            if (node.localJsxFactory !== undefined) updated.localJsxFactory = node.localJsxFactory;
+            if (node.localJsxNamespace !== undefined) updated.localJsxNamespace = node.localJsxNamespace;
             return updateNode(updated, node);
         }
 
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 70ddb7b791df1..b350af01bb036 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -769,7 +769,9 @@ namespace ts {
 
             // Prime the scanner.
             nextToken();
-            processReferenceComments(sourceFile);
+            // A member of ReadonlyArray<T> isn't assignable to a member of T[] (and prevents a direct cast) - but this is where we set up those members so they can be readonly in the future
+            processCommentPragmas(sourceFile as {} as PragmaContext, sourceText);
+            processPragmasIntoFields(sourceFile as {} as PragmaContext, reportPragmaDiagnostic);
 
             sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
             Debug.assert(token() === SyntaxKind.EndOfFileToken);
@@ -787,6 +789,10 @@ namespace ts {
             }
 
             return sourceFile;
+
+            function reportPragmaDiagnostic(pos: number, end: number, diagnostic: DiagnosticMessage) {
+                parseDiagnostics.push(createFileDiagnostic(sourceFile, pos, end, diagnostic));
+            }
         }
 
         function addJSDocComment<T extends HasJSDoc>(node: T): T {
@@ -6084,94 +6090,6 @@ namespace ts {
             return finishNode(node);
         }
 
-        function processReferenceComments(sourceFile: SourceFile): void {
-            const triviaScanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ false, LanguageVariant.Standard, sourceText);
-            const referencedFiles: FileReference[] = [];
-            const typeReferenceDirectives: FileReference[] = [];
-            const amdDependencies: { path: string; name: string }[] = [];
-            let amdModuleName: string;
-            let checkJsDirective: CheckJsDirective = undefined;
-
-            // Keep scanning all the leading trivia in the file until we get to something that
-            // isn't trivia.  Any single line comment will be analyzed to see if it is a
-            // reference comment.
-            while (true) {
-                const kind = triviaScanner.scan();
-                if (kind !== SyntaxKind.SingleLineCommentTrivia) {
-                    if (isTrivia(kind)) {
-                        continue;
-                    }
-                    else {
-                        break;
-                    }
-                }
-
-                const range = {
-                    kind: <SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia>triviaScanner.getToken(),
-                    pos: triviaScanner.getTokenPos(),
-                    end: triviaScanner.getTextPos(),
-                };
-
-                const comment = sourceText.substring(range.pos, range.end);
-                const referencePathMatchResult = getFileReferenceFromReferencePath(comment, range);
-                if (referencePathMatchResult) {
-                    const fileReference = referencePathMatchResult.fileReference;
-                    sourceFile.hasNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
-                    const diagnosticMessage = referencePathMatchResult.diagnosticMessage;
-                    if (fileReference) {
-                        if (referencePathMatchResult.isTypeReferenceDirective) {
-                            typeReferenceDirectives.push(fileReference);
-                        }
-                        else {
-                            referencedFiles.push(fileReference);
-                        }
-                    }
-                    if (diagnosticMessage) {
-                        parseDiagnostics.push(createFileDiagnostic(sourceFile, range.pos, range.end - range.pos, diagnosticMessage));
-                    }
-                }
-                else {
-                    const amdModuleNameRegEx = /^\/\/\/\s*<amd-module\s+name\s*=\s*('|")(.+?)\1/gim;
-                    const amdModuleNameMatchResult = amdModuleNameRegEx.exec(comment);
-                    if (amdModuleNameMatchResult) {
-                        if (amdModuleName) {
-                            parseDiagnostics.push(createFileDiagnostic(sourceFile, range.pos, range.end - range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments));
-                        }
-                        amdModuleName = amdModuleNameMatchResult[2];
-                    }
-
-                    const amdDependencyRegEx = /^\/\/\/\s*<amd-dependency\s/gim;
-                    const pathRegex = /\spath\s*=\s*('|")(.+?)\1/gim;
-                    const nameRegex = /\sname\s*=\s*('|")(.+?)\1/gim;
-                    const amdDependencyMatchResult = amdDependencyRegEx.exec(comment);
-                    if (amdDependencyMatchResult) {
-                        const pathMatchResult = pathRegex.exec(comment);
-                        const nameMatchResult = nameRegex.exec(comment);
-                        if (pathMatchResult) {
-                            const amdDependency = { path: pathMatchResult[2], name: nameMatchResult ? nameMatchResult[2] : undefined };
-                            amdDependencies.push(amdDependency);
-                        }
-                    }
-
-                    const checkJsDirectiveRegEx = /^\/\/\/?\s*(@ts-check|@ts-nocheck)\s*$/gim;
-                    const checkJsDirectiveMatchResult = checkJsDirectiveRegEx.exec(comment);
-                    if (checkJsDirectiveMatchResult) {
-                        checkJsDirective = {
-                            enabled: equateStringsCaseInsensitive(checkJsDirectiveMatchResult[1], "@ts-check"),
-                            end: range.end,
-                            pos: range.pos
-                        };
-                    }
-                }
-            }
-
-            sourceFile.referencedFiles = referencedFiles;
-            sourceFile.typeReferenceDirectives = typeReferenceDirectives;
-            sourceFile.amdDependencies = amdDependencies;
-            sourceFile.moduleName = amdModuleName;
-            sourceFile.checkJsDirective = checkJsDirective;
-        }
-
         function setExternalModuleIndicator(sourceFile: SourceFile) {
             sourceFile.externalModuleIndicator = forEach(sourceFile.statements, node =>
                 hasModifier(node, ModifierFlags.Export)
@@ -7552,4 +7470,225 @@ namespace ts {
     function isDeclarationFileName(fileName: string): boolean {
         return fileExtensionIs(fileName, Extension.Dts);
     }
+
+    /*@internal*/
+    export interface PragmaContext {
+        languageVersion: ScriptTarget;
+        pragmas?: PragmaMap;
+        checkJsDirective?: CheckJsDirective;
+        referencedFiles: FileReference[];
+        typeReferenceDirectives: FileReference[];
+        amdDependencies: AmdDependency[];
+        hasNoDefaultLib?: boolean;
+        moduleName?: string;
+    }
+
+    /*@internal*/
+    export function processCommentPragmas(context: PragmaContext, sourceText: string): void {
+        const triviaScanner = createScanner(context.languageVersion, /*skipTrivia*/ false, LanguageVariant.Standard, sourceText);
+        const pragmas: PragmaPsuedoMapEntry[] = [];
+
+        // Keep scanning all the leading trivia in the file until we get to something that
+        // isn't trivia.  Any single line comment will be analyzed to see if it is a
+        // reference comment.
+        while (true) {
+            const kind = triviaScanner.scan();
+            if (!isTrivia(kind)) {
+                break;
+            }
+
+            const range = {
+                kind: <SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia>triviaScanner.getToken(),
+                pos: triviaScanner.getTokenPos(),
+                end: triviaScanner.getTextPos(),
+            };
+
+            const comment = sourceText.substring(range.pos, range.end);
+            extractPragmas(pragmas, range, comment);
+        }
+
+        context.pragmas = createMap() as PragmaMap;
+        for (const pragma of pragmas) {
+            if (context.pragmas.has(pragma.name)) {
+                const currentValue = context.pragmas.get(pragma.name);
+                if (currentValue instanceof Array) {
+                    currentValue.push(pragma.args);
+                }
+                else {
+                    context.pragmas.set(pragma.name, [currentValue, pragma.args]);
+                }
+                continue;
+            }
+            context.pragmas.set(pragma.name, pragma.args);
+        }
+    }
+
+    /*@internal*/
+    type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void;
+
+    /*@internal*/
+    export function processPragmasIntoFields(context: PragmaContext, reportDiagnostic: PragmaDiagnosticReporter): void {
+        context.checkJsDirective = undefined;
+        context.referencedFiles = [];
+        context.typeReferenceDirectives = [];
+        context.amdDependencies = [];
+        context.hasNoDefaultLib = false;
+        context.pragmas.forEach((entryOrList, key) => {
+            // TODO: The below should be strongly type-guarded and not need casts/explicit annotations, since entryOrList is related to
+            // key and key is constrained to a union; but it's not (see GH#21483 for at least partial fix) :(
+            switch (key) {
+                case "reference": {
+                    const referencedFiles = context.referencedFiles;
+                    const typeReferenceDirectives = context.typeReferenceDirectives;
+                    forEach(toArray(entryOrList), (arg: PragmaPsuedoMap["reference"]) => {
+                        if (arg.arguments["no-default-lib"]) {
+                            context.hasNoDefaultLib = true;
+                        }
+                        else if (arg.arguments.types) {
+                            typeReferenceDirectives.push({ pos: arg.arguments.types.pos, end: arg.arguments.types.end, fileName: arg.arguments.types.value });
+                        }
+                        else if (arg.arguments.path) {
+                            referencedFiles.push({ pos: arg.arguments.path.pos, end: arg.arguments.path.end, fileName: arg.arguments.path.value });
+                        }
+                        else {
+                            reportDiagnostic(arg.range.pos, arg.range.end - arg.range.pos, Diagnostics.Invalid_reference_directive_syntax);
+                        }
+                    });
+                    break;
+                }
+                case "amd-dependency": {
+                    context.amdDependencies = map(
+                        toArray(entryOrList),
+                        ({ arguments: { name, path } }: PragmaPsuedoMap["amd-dependency"]) => ({ name, path })
+                    );
+                    break;
+                }
+                case "amd-module": {
+                    if (entryOrList instanceof Array) {
+                        for (const entry of entryOrList) {
+                            if (context.moduleName) {
+                                // TODO: It's probably fine to issue this diagnostic on all instances of the pragma
+                                reportDiagnostic(entry.range.pos, entry.range.end - entry.range.pos, Diagnostics.An_AMD_module_cannot_have_multiple_name_assignments);
+                            }
+                            context.moduleName = (entry as PragmaPsuedoMap["amd-module"]).arguments.name;
+                        }
+                    }
+                    else {
+                        context.moduleName = (entryOrList as PragmaPsuedoMap["amd-module"]).arguments.name;
+                    }
+                    break;
+                }
+                case "ts-nocheck":
+                case "ts-check": {
+                    // _last_ of either nocheck or check in a file is the "winner"
+                    forEach(toArray(entryOrList), entry => {
+                        if (!context.checkJsDirective || entry.range.pos > context.checkJsDirective.pos) {
+                            context.checkJsDirective = {
+                                enabled: key === "ts-check",
+                                end: entry.range.end,
+                                pos: entry.range.pos
+                            };
+                        }
+                    });
+                    break;
+                }
+                case "jsx": return; // Accessed directly
+                default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future?
+            }
+        });
+    }
+
+    const namedArgRegExCache = createMap<RegExp>();
+    function getNamedArgRegEx(name: string) {
+        if (namedArgRegExCache.has(name)) {
+            return namedArgRegExCache.get(name);
+        }
+        const result = new RegExp(`(\\s${name}\\s*=\\s*)('|")(.+?)\\2`, "im");
+        namedArgRegExCache.set(name, result);
+        return result;
+    }
+
+    const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/im;
+    const singleLinePragmaRegEx = /^\/\/\/?\s*@(\S+)\s*(.*)\s*$/im;
+    function extractPragmas(pragmas: PragmaPsuedoMapEntry[], range: CommentRange, text: string) {
+        const tripleSlash = tripleSlashXMLCommentStartRegEx.exec(text);
+        if (tripleSlash) {
+            const name = tripleSlash[1].toLowerCase() as keyof PragmaPsuedoMap; // Technically unsafe cast, but we do it so the below check to make it safe typechecks
+            const pragma = commentPragmas[name] as PragmaDefinition;
+            if (!pragma || !(pragma.kind & PragmaKindFlags.TripleSlashXML)) {
+                return;
+            }
+            if (pragma.args) {
+                const argument: {[index: string]: string | {value: string, pos: number, end: number}} = {};
+                for (const arg of pragma.args) {
+                    const matcher = getNamedArgRegEx(arg.name);
+                    const matchResult = matcher.exec(text);
+                    if (!matchResult && !arg.optional) {
+                        return; // Missing required argument, don't parse
+                    }
+                    else if (matchResult) {
+                        if (arg.captureSpan) {
+                            const startPos = range.pos + matchResult.index + matchResult[1].length + matchResult[2].length;
+                            argument[arg.name] = {
+                                value: matchResult[3],
+                                pos: startPos,
+                                end: startPos + matchResult[3].length
+                            };
+                        }
+                        else {
+                            argument[arg.name] = matchResult[3];
+                        }
+                    }
+                }
+                pragmas.push({ name, args: { arguments: argument, range } } as PragmaPsuedoMapEntry);
+            }
+            else {
+                pragmas.push({ name, args: { arguments: {}, range } } as PragmaPsuedoMapEntry);
+            }
+            return;
+        }
+
+        const singleLine = singleLinePragmaRegEx.exec(text);
+        if (singleLine) {
+            return addPragmaForMatch(pragmas, range, PragmaKindFlags.SingleLine, singleLine);
+        }
+
+        const multiLinePragmaRegEx = /\s*@(\S+)\s*(.*)\s*$/gim; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating)
+        let multiLineMatch: RegExpExecArray;
+        while (multiLineMatch = multiLinePragmaRegEx.exec(text)) {
+            addPragmaForMatch(pragmas, range, PragmaKindFlags.MultiLine, multiLineMatch);
+        }
+    }
+
+    function addPragmaForMatch(pragmas: PragmaPsuedoMapEntry[], range: CommentRange, kind: PragmaKindFlags, match: RegExpExecArray) {
+        if (!match) return;
+        const name = match[1].toLowerCase() as keyof PragmaPsuedoMap; // Technically unsafe cast, but we do it so they below check to make it safe typechecks
+        const pragma = commentPragmas[name] as PragmaDefinition;
+        if (!pragma || !(pragma.kind & kind)) {
+            return;
+        }
+        const args = match[2]; // Split on spaces and match up positionally with definition
+        const argument = getNamedPragmaArguments(pragma, args);
+        if (argument === "fail") return; // Missing required argument, fail to parse it
+        pragmas.push({ name, args: { arguments: argument, range } } as PragmaPsuedoMapEntry);
+        return;
+    }
+
+    function getNamedPragmaArguments(pragma: PragmaDefinition, text: string | undefined): {[index: string]: string} | "fail" {
+        if (!text) return {};
+        if (!pragma.args) return {};
+        const args = text.split(/\s+/);
+        const argMap: {[index: string]: string} = {};
+        for (let i = 0; i < pragma.args.length; i++) {
+            const argument = pragma.args[i];
+            if (!args[i] && !argument.optional) {
+                return "fail";
+            }
+            if (argument.captureSpan) {
+                return Debug.fail("Capture spans not yet implemented for non-xml pragmas");
+            }
+            argMap[argument.name] = args[i];
+        }
+        return argMap;
+    }
 }
diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts
index 94e41af9c2890..25e1ae02ef829 100644
--- a/src/compiler/transformers/jsx.ts
+++ b/src/compiler/transformers/jsx.ts
@@ -122,7 +122,7 @@ namespace ts {
             }
 
             const element = createExpressionForJsxElement(
-                context.getEmitResolver().getJsxFactoryEntity(),
+                context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
                 compilerOptions.reactNamespace,
                 tagName,
                 objectProperties,
@@ -140,7 +140,7 @@ namespace ts {
 
         function visitJsxOpeningFragment(node: JsxOpeningFragment, children: ReadonlyArray<JsxChild>, isChild: boolean, location: TextRange) {
             const element = createExpressionForJsxFragment(
-                context.getEmitResolver().getJsxFactoryEntity(),
+                context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
                 compilerOptions.reactNamespace,
                 mapDefined(children, transformJsxChildToExpression),
                 node,
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 0e1f08a69d840..7e7f264293f55 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2568,6 +2568,9 @@ namespace ts {
         /* @internal */ ambientModuleNames: ReadonlyArray<string>;
         /* @internal */ checkJsDirective: CheckJsDirective | undefined;
         /* @internal */ version: string;
+        /* @internal */ pragmas: PragmaMap;
+        /* @internal */ localJsxNamespace?: __String;
+        /* @internal */ localJsxFactory?: EntityName;
     }
 
     export interface Bundle extends Node {
@@ -2934,7 +2937,7 @@ namespace ts {
         /* @internal */ isArrayLikeType(type: Type): boolean;
         /* @internal */ getAllPossiblePropertiesOfTypes(type: ReadonlyArray<Type>): Symbol[];
         /* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags, excludeGlobals: boolean): Symbol | undefined;
-        /* @internal */ getJsxNamespace(): string;
+        /* @internal */ getJsxNamespace(location?: Node): string;
 
         /**
          * Note that this will return undefined in the following case:
@@ -3208,7 +3211,7 @@ namespace ts {
         getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[];
         isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
         writeLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, writer: EmitTextWriter): void;
-        getJsxFactoryEntity(): EntityName;
+        getJsxFactoryEntity(location?: Node): EntityName;
     }
 
     export const enum SymbolFlags {
@@ -5123,4 +5126,127 @@ namespace ts {
         Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
         IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets,
     }
+
+    /* @internal */
+    export const enum PragmaKindFlags {
+        None            =      0,
+        /**
+         * Triple slash comment of the form
+         * /// <pragma-name argname="value" />
+         */
+        TripleSlashXML  = 1 << 0,
+        /**
+         * Single line comment of the form
+         * // @pragma-name argval1 argval2
+         * or
+         * /// @pragma-name argval1 argval2
+         */
+        SingleLine      = 1 << 1,
+        /**
+         * Multiline non-jsdoc pragma of the form
+         * /* @pragma-name argval1 argval2 * /
+         */
+        MultiLine       = 1 << 2,
+        All = TripleSlashXML | SingleLine | MultiLine,
+        Default = All,
+    }
+
+    /* @internal */
+    interface PragmaArgumentSpecification<TName extends string> {
+        name: TName; // Determines the name of the key in the resulting parsed type, type parameter to cause literal type inference
+        optional?: boolean;
+        captureSpan?: boolean;
+    }
+
+    /* @internal */
+    export interface PragmaDefinition<T1 extends string = string, T2 extends string = string, T3 extends string = string> {
+        args?: [PragmaArgumentSpecification<T1>] | [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>] | [PragmaArgumentSpecification<T1>, PragmaArgumentSpecification<T2>, PragmaArgumentSpecification<T3>];
+        // If not present, defaults to PragmaKindFlags.Default
+        kind?: PragmaKindFlags;
+    }
+
+    /**
+     * This function only exists to cause exact types to be inferred for all the literals within `commentPragmas`
+     */
+    /* @internal */
+    function _contextuallyTypePragmas<T extends {[name: string]: PragmaDefinition<K1, K2, K3>}, K1 extends string, K2 extends string, K3 extends string>(args: T): T {
+        return args;
+    }
+
+    // While not strictly a type, this is here because `PragmaMap` needs to be here to be used with `SourceFile`, and we don't
+    //  fancy effectively defining it twice, once in value-space and once in type-space
+    /* @internal */
+    export const commentPragmas = _contextuallyTypePragmas({
+        "reference": {
+            args: [
+                { name: "types", optional: true, captureSpan: true },
+                { name: "path", optional: true, captureSpan: true },
+                { name: "no-default-lib", optional: true }
+            ],
+            kind: PragmaKindFlags.TripleSlashXML
+        },
+        "amd-dependency": {
+            args: [{ name: "path" }, { name: "name", optional: true }],
+            kind: PragmaKindFlags.TripleSlashXML
+        },
+        "amd-module": {
+            args: [{ name: "name" }],
+            kind: PragmaKindFlags.TripleSlashXML
+        },
+        "ts-check": {
+            kind: PragmaKindFlags.SingleLine
+        },
+        "ts-nocheck": {
+            kind: PragmaKindFlags.SingleLine
+        },
+        "jsx": {
+            args: [{ name: "factory" }],
+            kind: PragmaKindFlags.MultiLine
+        },
+    });
+
+    /* @internal */
+    type PragmaArgTypeMaybeCapture<TDesc> = TDesc extends {captureSpan: true} ? {value: string, pos: number, end: number} : string;
+
+    /* @internal */
+    type PragmaArgTypeOptional<TDesc, TName extends string> =
+        TDesc extends {optional: true}
+            ? {[K in TName]?: PragmaArgTypeMaybeCapture<TDesc>}
+            : {[K in TName]: PragmaArgTypeMaybeCapture<TDesc>};
+
+    /**
+     * Maps a pragma definition into the desired shape for its arguments object
+     * Maybe the below is a good argument for types being iterable on struture in some way.
+     */
+    /* @internal */
+    type PragmaArgumentType<T extends PragmaDefinition> =
+        T extends { args: [PragmaArgumentSpecification<infer TName1>, PragmaArgumentSpecification<infer TName2>, PragmaArgumentSpecification<infer TName3>] }
+        ? PragmaArgTypeOptional<T["args"][0], TName1> & PragmaArgTypeOptional<T["args"][1], TName2> & PragmaArgTypeOptional<T["args"][2], TName3>
+        : T extends { args: [PragmaArgumentSpecification<infer TName1>, PragmaArgumentSpecification<infer TName2>] }
+            ? PragmaArgTypeOptional<T["args"][0], TName1> & PragmaArgTypeOptional<T["args"][1], TName2>
+            : T extends { args: [PragmaArgumentSpecification<infer TName>] }
+                ? PragmaArgTypeOptional<T["args"][0], TName>
+                : object;
+    // The above fallback to `object` when there's no args to allow `{}` (as intended), but not the number 2, for example
+    // TODO: Swap to `undefined` for a cleaner API once strictNullChecks is enabled
+
+    type ConcretePragmaSpecs = typeof commentPragmas;
+
+    /* @internal */
+    export type PragmaPsuedoMap = {[K in keyof ConcretePragmaSpecs]?: {arguments: PragmaArgumentType<ConcretePragmaSpecs[K]>, range: CommentRange}};
+
+    /* @internal */
+    export type PragmaPsuedoMapEntry = {[K in keyof PragmaPsuedoMap]: {name: K, args: PragmaPsuedoMap[K]}}[keyof PragmaPsuedoMap];
+
+    /**
+     * A strongly-typed es6 map of pragma entries, the values of which are either a single argument
+     * value (if only one was found), or an array of multiple argument values if the pragma is present
+     * in multiple places
+     */
+    /* @internal */
+    export interface PragmaMap extends Map<PragmaPsuedoMap[keyof PragmaPsuedoMap] | PragmaPsuedoMap[keyof PragmaPsuedoMap][]> {
+        set<TKey extends keyof PragmaPsuedoMap>(key: TKey, value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][]): this;
+        get<TKey extends keyof PragmaPsuedoMap>(key: TKey): PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][];
+        forEach(action: <TKey extends keyof PragmaPsuedoMap>(value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][], key: TKey) => void): void;
+    }
 }
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index c6861380d8dd0..d8fc641f36678 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1996,40 +1996,6 @@ namespace ts {
         return undefined;
     }
 
-    export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult {
-        const simpleReferenceRegEx = /^\/\/\/\s*<reference\s+/gim;
-        const isNoDefaultLibRegEx = new RegExp(defaultLibReferenceRegEx.source, "gim");
-        if (simpleReferenceRegEx.test(comment)) {
-            if (isNoDefaultLibRegEx.test(comment)) {
-                return { isNoDefaultLib: true };
-            }
-            else {
-                const refMatchResult = fullTripleSlashReferencePathRegEx.exec(comment);
-                const refLibResult = !refMatchResult && fullTripleSlashReferenceTypeReferenceDirectiveRegEx.exec(comment);
-                const match = refMatchResult || refLibResult;
-                if (match) {
-                    const pos = commentRange.pos + match[1].length + match[2].length;
-                    return {
-                        fileReference: {
-                            pos,
-                            end: pos + match[3].length,
-                            fileName: match[3]
-                        },
-                        isNoDefaultLib: false,
-                        isTypeReferenceDirective: !!refLibResult
-                    };
-                }
-
-                return {
-                    diagnosticMessage: Diagnostics.Invalid_reference_directive_syntax,
-                    isNoDefaultLib: false
-                };
-            }
-        }
-
-        return undefined;
-    }
-
     export function isKeyword(token: SyntaxKind): boolean {
         return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
     }
diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts
index 9f9bc959bf0d6..74a2aba76bb51 100644
--- a/src/services/codefixes/importFixes.ts
+++ b/src/services/codefixes/importFixes.ts
@@ -663,7 +663,7 @@ namespace ts.codefix {
             const parent = token.parent;
             const isNodeOpeningLikeElement = isJsxOpeningLikeElement(parent);
             if ((isJsxOpeningLikeElement && (<JsxOpeningLikeElement>parent).tagName === token) || parent.kind === SyntaxKind.JsxOpeningFragment) {
-                umdSymbol = checker.resolveName(checker.getJsxNamespace(),
+                umdSymbol = checker.resolveName(checker.getJsxNamespace(parent),
                     isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>parent).tagName : parent, SymbolFlags.Value, /*excludeGlobals*/ false);
             }
         }
diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts
index a99683d45effa..fb138a878663f 100644
--- a/src/services/preProcess.ts
+++ b/src/services/preProcess.ts
@@ -1,10 +1,17 @@
 namespace ts {
     export function preProcessFile(sourceText: string, readImportFiles = true, detectJavaScriptImports = false): PreProcessedFileInfo {
-        const referencedFiles: FileReference[] = [];
-        const typeReferenceDirectives: FileReference[] = [];
+        const pragmaContext: PragmaContext = {
+            languageVersion: ScriptTarget.ES5, // controls weather the token scanner considers unicode identifiers or not - shouldn't matter, since we're only using it for trivia
+            pragmas: undefined,
+            checkJsDirective: undefined,
+            referencedFiles: [],
+            typeReferenceDirectives: [],
+            amdDependencies: [],
+            hasNoDefaultLib: undefined,
+            moduleName: undefined
+        };
         const importedFiles: FileReference[] = [];
         let ambientExternalModules: { ref: FileReference, depth: number }[];
-        let isNoDefaultLib = false;
         let braceNesting = 0;
         // assume that text represent an external module if it contains at least one top level import/export
         // ambient modules that are found inside external modules are interpreted as module augmentations
@@ -21,25 +28,6 @@ namespace ts {
             return token;
         }
 
-        function processTripleSlashDirectives(): void {
-            const commentRanges = getLeadingCommentRanges(sourceText, 0);
-            forEach(commentRanges, commentRange => {
-                const comment = sourceText.substring(commentRange.pos, commentRange.end);
-                const referencePathMatchResult = getFileReferenceFromReferencePath(comment, commentRange);
-                if (referencePathMatchResult) {
-                    isNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
-                    const fileReference = referencePathMatchResult.fileReference;
-                    if (fileReference) {
-                        const collection = referencePathMatchResult.isTypeReferenceDirective
-                            ? typeReferenceDirectives
-                            : referencedFiles;
-
-                        collection.push(fileReference);
-                    }
-                }
-            });
-        }
-
         function getFileReference() {
             const fileName = scanner.getTokenValue();
             const pos = scanner.getTokenPos();
@@ -328,7 +316,8 @@ namespace ts {
         if (readImportFiles) {
             processImports();
         }
-        processTripleSlashDirectives();
+        processCommentPragmas(pragmaContext, sourceText);
+        processPragmasIntoFields(pragmaContext, noop);
         if (externalModule) {
             // for external modules module all nested ambient modules are augmentations
             if (ambientExternalModules) {
@@ -337,7 +326,7 @@ namespace ts {
                     importedFiles.push(decl.ref);
                 }
             }
-            return { referencedFiles, typeReferenceDirectives, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: undefined };
+            return { referencedFiles: pragmaContext.referencedFiles, typeReferenceDirectives: pragmaContext.typeReferenceDirectives, importedFiles, isLibFile: pragmaContext.hasNoDefaultLib, ambientExternalModules: undefined };
         }
         else {
             // for global scripts ambient modules still can have augmentations - look for ambient modules with depth > 0
@@ -355,7 +344,7 @@ namespace ts {
                     }
                 }
             }
-            return { referencedFiles, typeReferenceDirectives, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: ambientModuleNames };
+            return { referencedFiles: pragmaContext.referencedFiles, typeReferenceDirectives: pragmaContext.typeReferenceDirectives, importedFiles, isLibFile: pragmaContext.hasNoDefaultLib, ambientExternalModules: ambientModuleNames };
         }
     }
 }
diff --git a/src/services/services.ts b/src/services/services.ts
index caa927cf80d83..dd46d986a7901 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -651,6 +651,9 @@ namespace ts {
         public ambientModuleNames: string[];
         public checkJsDirective: CheckJsDirective | undefined;
         public possiblyContainDynamicImport: boolean;
+        public pragmas: PragmaMap;
+        public localJsxFactory: EntityName;
+        public localJsxNamespace: __String;
 
         constructor(kind: SyntaxKind, pos: number, end: number) {
             super(kind, pos, end);
diff --git a/tests/baselines/reference/inlineJsxFactoryDeclarations.js b/tests/baselines/reference/inlineJsxFactoryDeclarations.js
new file mode 100644
index 0000000000000..958c43af83f68
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryDeclarations.js
@@ -0,0 +1,74 @@
+//// [tests/cases/conformance/jsx/inline/inlineJsxFactoryDeclarations.tsx] ////
+
+//// [renderer.d.ts]
+declare global {
+    namespace JSX {
+        interface IntrinsicElements {
+            [e: string]: any;
+        }
+    }
+}
+export function dom(): void;
+export function otherdom(): void;
+export function createElement(): void;
+export { dom as default };
+//// [otherreacty.tsx]
+/** @jsx React.createElement */
+import * as React from "./renderer";
+<h></h>
+//// [other.tsx]
+/** @jsx h */
+import { dom as h } from "./renderer"
+export const prerendered = <h></h>;
+//// [othernoalias.tsx]
+/** @jsx otherdom */
+import { otherdom } from "./renderer"
+export const prerendered2 = <h></h>;
+//// [reacty.tsx]
+import React from "./renderer"
+export const prerendered3 = <h></h>;
+
+//// [index.tsx]
+/** @jsx dom */
+import { dom } from "./renderer"
+<h></h>
+export * from "./other";
+export * from "./othernoalias";
+export * from "./reacty";
+
+
+//// [otherreacty.js]
+"use strict";
+exports.__esModule = true;
+/** @jsx React.createElement */
+var React = require("./renderer");
+React.createElement("h", null);
+//// [other.js]
+"use strict";
+exports.__esModule = true;
+/** @jsx h */
+var renderer_1 = require("./renderer");
+exports.prerendered = renderer_1.dom("h", null);
+//// [othernoalias.js]
+"use strict";
+exports.__esModule = true;
+/** @jsx otherdom */
+var renderer_1 = require("./renderer");
+exports.prerendered2 = renderer_1.otherdom("h", null);
+//// [reacty.js]
+"use strict";
+exports.__esModule = true;
+var renderer_1 = require("./renderer");
+exports.prerendered3 = renderer_1["default"].createElement("h", null);
+//// [index.js]
+"use strict";
+function __export(m) {
+    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}
+exports.__esModule = true;
+/** @jsx dom */
+var renderer_1 = require("./renderer");
+renderer_1.dom("h", null);
+__export(require("./other"));
+__export(require("./othernoalias"));
+__export(require("./reacty"));
diff --git a/tests/baselines/reference/inlineJsxFactoryDeclarations.symbols b/tests/baselines/reference/inlineJsxFactoryDeclarations.symbols
new file mode 100644
index 0000000000000..43a53ec9df277
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryDeclarations.symbols
@@ -0,0 +1,80 @@
+=== tests/cases/conformance/jsx/inline/renderer.d.ts ===
+declare global {
+>global : Symbol(global, Decl(renderer.d.ts, 0, 0))
+
+    namespace JSX {
+>JSX : Symbol(JSX, Decl(renderer.d.ts, 0, 16))
+
+        interface IntrinsicElements {
+>IntrinsicElements : Symbol(IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+            [e: string]: any;
+>e : Symbol(e, Decl(renderer.d.ts, 3, 13))
+        }
+    }
+}
+export function dom(): void;
+>dom : Symbol(dom, Decl(renderer.d.ts, 6, 1))
+
+export function otherdom(): void;
+>otherdom : Symbol(otherdom, Decl(renderer.d.ts, 7, 28))
+
+export function createElement(): void;
+>createElement : Symbol(createElement, Decl(renderer.d.ts, 8, 33))
+
+export { dom as default };
+>dom : Symbol(default, Decl(renderer.d.ts, 10, 8))
+>default : Symbol(default, Decl(renderer.d.ts, 10, 8))
+
+=== tests/cases/conformance/jsx/inline/otherreacty.tsx ===
+/** @jsx React.createElement */
+import * as React from "./renderer";
+>React : Symbol(React, Decl(otherreacty.tsx, 1, 6))
+
+<h></h>
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+=== tests/cases/conformance/jsx/inline/other.tsx ===
+/** @jsx h */
+import { dom as h } from "./renderer"
+>dom : Symbol(h, Decl(other.tsx, 1, 8))
+>h : Symbol(h, Decl(other.tsx, 1, 8))
+
+export const prerendered = <h></h>;
+>prerendered : Symbol(prerendered, Decl(other.tsx, 2, 12))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+=== tests/cases/conformance/jsx/inline/othernoalias.tsx ===
+/** @jsx otherdom */
+import { otherdom } from "./renderer"
+>otherdom : Symbol(otherdom, Decl(othernoalias.tsx, 1, 8))
+
+export const prerendered2 = <h></h>;
+>prerendered2 : Symbol(prerendered2, Decl(othernoalias.tsx, 2, 12))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+=== tests/cases/conformance/jsx/inline/reacty.tsx ===
+import React from "./renderer"
+>React : Symbol(React, Decl(reacty.tsx, 0, 6))
+
+export const prerendered3 = <h></h>;
+>prerendered3 : Symbol(prerendered3, Decl(reacty.tsx, 1, 12))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+=== tests/cases/conformance/jsx/inline/index.tsx ===
+/** @jsx dom */
+import { dom } from "./renderer"
+>dom : Symbol(dom, Decl(index.tsx, 1, 8))
+
+<h></h>
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+export * from "./other";
+export * from "./othernoalias";
+export * from "./reacty";
+
diff --git a/tests/baselines/reference/inlineJsxFactoryDeclarations.types b/tests/baselines/reference/inlineJsxFactoryDeclarations.types
new file mode 100644
index 0000000000000..13c382fe45cea
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryDeclarations.types
@@ -0,0 +1,85 @@
+=== tests/cases/conformance/jsx/inline/renderer.d.ts ===
+declare global {
+>global : any
+
+    namespace JSX {
+>JSX : any
+
+        interface IntrinsicElements {
+>IntrinsicElements : IntrinsicElements
+
+            [e: string]: any;
+>e : string
+        }
+    }
+}
+export function dom(): void;
+>dom : () => void
+
+export function otherdom(): void;
+>otherdom : () => void
+
+export function createElement(): void;
+>createElement : () => void
+
+export { dom as default };
+>dom : () => void
+>default : () => void
+
+=== tests/cases/conformance/jsx/inline/otherreacty.tsx ===
+/** @jsx React.createElement */
+import * as React from "./renderer";
+>React : typeof React
+
+<h></h>
+><h></h> : any
+>h : any
+>h : any
+
+=== tests/cases/conformance/jsx/inline/other.tsx ===
+/** @jsx h */
+import { dom as h } from "./renderer"
+>dom : () => void
+>h : () => void
+
+export const prerendered = <h></h>;
+>prerendered : any
+><h></h> : any
+>h : () => void
+>h : () => void
+
+=== tests/cases/conformance/jsx/inline/othernoalias.tsx ===
+/** @jsx otherdom */
+import { otherdom } from "./renderer"
+>otherdom : () => void
+
+export const prerendered2 = <h></h>;
+>prerendered2 : any
+><h></h> : any
+>h : any
+>h : any
+
+=== tests/cases/conformance/jsx/inline/reacty.tsx ===
+import React from "./renderer"
+>React : () => void
+
+export const prerendered3 = <h></h>;
+>prerendered3 : any
+><h></h> : any
+>h : any
+>h : any
+
+=== tests/cases/conformance/jsx/inline/index.tsx ===
+/** @jsx dom */
+import { dom } from "./renderer"
+>dom : () => void
+
+<h></h>
+><h></h> : any
+>h : any
+>h : any
+
+export * from "./other";
+export * from "./othernoalias";
+export * from "./reacty";
+
diff --git a/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.js b/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.js
new file mode 100644
index 0000000000000..fef2e38ee3bd0
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.js
@@ -0,0 +1,32 @@
+//// [tests/cases/conformance/jsx/inline/inlineJsxFactoryOverridesCompilerOption.tsx] ////
+
+//// [renderer.d.ts]
+declare global {
+    namespace JSX {
+        interface IntrinsicElements {
+            [e: string]: any;
+        }
+    }
+}
+export function dom(): void;
+export { dom as p };
+//// [reacty.tsx]
+/** @jsx dom */
+import {dom} from "./renderer";
+<h></h>
+//// [index.tsx]
+import { p } from "./renderer";
+<h></h>
+
+
+//// [reacty.js]
+"use strict";
+exports.__esModule = true;
+/** @jsx dom */
+var renderer_1 = require("./renderer");
+renderer_1.dom("h", null);
+//// [index.js]
+"use strict";
+exports.__esModule = true;
+var renderer_1 = require("./renderer");
+renderer_1.p("h", null);
diff --git a/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.symbols b/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.symbols
new file mode 100644
index 0000000000000..d3c513c587665
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.symbols
@@ -0,0 +1,39 @@
+=== tests/cases/conformance/jsx/inline/renderer.d.ts ===
+declare global {
+>global : Symbol(global, Decl(renderer.d.ts, 0, 0))
+
+    namespace JSX {
+>JSX : Symbol(JSX, Decl(renderer.d.ts, 0, 16))
+
+        interface IntrinsicElements {
+>IntrinsicElements : Symbol(IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+            [e: string]: any;
+>e : Symbol(e, Decl(renderer.d.ts, 3, 13))
+        }
+    }
+}
+export function dom(): void;
+>dom : Symbol(dom, Decl(renderer.d.ts, 6, 1))
+
+export { dom as p };
+>dom : Symbol(p, Decl(renderer.d.ts, 8, 8))
+>p : Symbol(p, Decl(renderer.d.ts, 8, 8))
+
+=== tests/cases/conformance/jsx/inline/reacty.tsx ===
+/** @jsx dom */
+import {dom} from "./renderer";
+>dom : Symbol(dom, Decl(reacty.tsx, 1, 8))
+
+<h></h>
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+=== tests/cases/conformance/jsx/inline/index.tsx ===
+import { p } from "./renderer";
+>p : Symbol(p, Decl(index.tsx, 0, 8))
+
+<h></h>
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
diff --git a/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.types b/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.types
new file mode 100644
index 0000000000000..36b50405dcb08
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.types
@@ -0,0 +1,41 @@
+=== tests/cases/conformance/jsx/inline/renderer.d.ts ===
+declare global {
+>global : any
+
+    namespace JSX {
+>JSX : any
+
+        interface IntrinsicElements {
+>IntrinsicElements : IntrinsicElements
+
+            [e: string]: any;
+>e : string
+        }
+    }
+}
+export function dom(): void;
+>dom : () => void
+
+export { dom as p };
+>dom : () => void
+>p : () => void
+
+=== tests/cases/conformance/jsx/inline/reacty.tsx ===
+/** @jsx dom */
+import {dom} from "./renderer";
+>dom : () => void
+
+<h></h>
+><h></h> : any
+>h : any
+>h : any
+
+=== tests/cases/conformance/jsx/inline/index.tsx ===
+import { p } from "./renderer";
+>p : () => void
+
+<h></h>
+><h></h> : any
+>h : any
+>h : any
+
diff --git a/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.errors.txt b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.errors.txt
new file mode 100644
index 0000000000000..b70ddf5f9b9aa
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.errors.txt
@@ -0,0 +1,26 @@
+tests/cases/conformance/jsx/inline/index.tsx(3,1): error TS17017: JSX fragment is not supported when using an inline JSX factory pragma
+tests/cases/conformance/jsx/inline/reacty.tsx(3,1): error TS17017: JSX fragment is not supported when using an inline JSX factory pragma
+
+
+==== tests/cases/conformance/jsx/inline/renderer.d.ts (0 errors) ====
+    declare global {
+        namespace JSX {
+            interface IntrinsicElements {
+                [e: string]: any;
+            }
+        }
+    }
+    export function dom(): void;
+    export function createElement(): void;
+==== tests/cases/conformance/jsx/inline/reacty.tsx (1 errors) ====
+    /** @jsx React.createElement */
+    import * as React from "./renderer";
+    <><h></h></>
+    ~~~~~~~~~~~~
+!!! error TS17017: JSX fragment is not supported when using an inline JSX factory pragma
+==== tests/cases/conformance/jsx/inline/index.tsx (1 errors) ====
+    /** @jsx dom */
+    import { dom } from "./renderer";
+    <><h></h></>
+    ~~~~~~~~~~~~
+!!! error TS17017: JSX fragment is not supported when using an inline JSX factory pragma
\ No newline at end of file
diff --git a/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.js b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.js
new file mode 100644
index 0000000000000..3af54bc04e600
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.js
@@ -0,0 +1,35 @@
+//// [tests/cases/conformance/jsx/inline/inlineJsxFactoryWithFragmentIsError.tsx] ////
+
+//// [renderer.d.ts]
+declare global {
+    namespace JSX {
+        interface IntrinsicElements {
+            [e: string]: any;
+        }
+    }
+}
+export function dom(): void;
+export function createElement(): void;
+//// [reacty.tsx]
+/** @jsx React.createElement */
+import * as React from "./renderer";
+<><h></h></>
+//// [index.tsx]
+/** @jsx dom */
+import { dom } from "./renderer";
+<><h></h></>
+
+//// [reacty.js]
+"use strict";
+exports.__esModule = true;
+/** @jsx React.createElement */
+var React = require("./renderer");
+React.createElement(React.Fragment, null,
+    React.createElement("h", null));
+//// [index.js]
+"use strict";
+exports.__esModule = true;
+/** @jsx dom */
+var renderer_1 = require("./renderer");
+renderer_1.dom(React.Fragment, null,
+    renderer_1.dom("h", null));
diff --git a/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.symbols b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.symbols
new file mode 100644
index 0000000000000..0453bd2610538
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.symbols
@@ -0,0 +1,39 @@
+=== tests/cases/conformance/jsx/inline/renderer.d.ts ===
+declare global {
+>global : Symbol(global, Decl(renderer.d.ts, 0, 0))
+
+    namespace JSX {
+>JSX : Symbol(JSX, Decl(renderer.d.ts, 0, 16))
+
+        interface IntrinsicElements {
+>IntrinsicElements : Symbol(IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+            [e: string]: any;
+>e : Symbol(e, Decl(renderer.d.ts, 3, 13))
+        }
+    }
+}
+export function dom(): void;
+>dom : Symbol(dom, Decl(renderer.d.ts, 6, 1))
+
+export function createElement(): void;
+>createElement : Symbol(createElement, Decl(renderer.d.ts, 7, 28))
+
+=== tests/cases/conformance/jsx/inline/reacty.tsx ===
+/** @jsx React.createElement */
+import * as React from "./renderer";
+>React : Symbol(React, Decl(reacty.tsx, 1, 6))
+
+<><h></h></>
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
+=== tests/cases/conformance/jsx/inline/index.tsx ===
+/** @jsx dom */
+import { dom } from "./renderer";
+>dom : Symbol(dom, Decl(index.tsx, 1, 8))
+
+<><h></h></>
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+>h : Symbol(JSX.IntrinsicElements, Decl(renderer.d.ts, 1, 19))
+
diff --git a/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.types b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.types
new file mode 100644
index 0000000000000..e7cd48810b0a7
--- /dev/null
+++ b/tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.types
@@ -0,0 +1,43 @@
+=== tests/cases/conformance/jsx/inline/renderer.d.ts ===
+declare global {
+>global : any
+
+    namespace JSX {
+>JSX : any
+
+        interface IntrinsicElements {
+>IntrinsicElements : IntrinsicElements
+
+            [e: string]: any;
+>e : string
+        }
+    }
+}
+export function dom(): void;
+>dom : () => void
+
+export function createElement(): void;
+>createElement : () => void
+
+=== tests/cases/conformance/jsx/inline/reacty.tsx ===
+/** @jsx React.createElement */
+import * as React from "./renderer";
+>React : typeof React
+
+<><h></h></>
+><><h></h></> : any
+><h></h> : any
+>h : any
+>h : any
+
+=== tests/cases/conformance/jsx/inline/index.tsx ===
+/** @jsx dom */
+import { dom } from "./renderer";
+>dom : () => void
+
+<><h></h></>
+><><h></h></> : any
+><h></h> : any
+>h : any
+>h : any
+
diff --git a/tests/cases/conformance/jsx/inline/inlineJsxFactoryDeclarations.tsx b/tests/cases/conformance/jsx/inline/inlineJsxFactoryDeclarations.tsx
new file mode 100644
index 0000000000000..f574c7e9db9b5
--- /dev/null
+++ b/tests/cases/conformance/jsx/inline/inlineJsxFactoryDeclarations.tsx
@@ -0,0 +1,36 @@
+// @jsx: react
+// @filename: renderer.d.ts
+declare global {
+    namespace JSX {
+        interface IntrinsicElements {
+            [e: string]: any;
+        }
+    }
+}
+export function dom(): void;
+export function otherdom(): void;
+export function createElement(): void;
+export { dom as default };
+// @filename: otherreacty.tsx
+/** @jsx React.createElement */
+import * as React from "./renderer";
+<h></h>
+// @filename: other.tsx
+/** @jsx h */
+import { dom as h } from "./renderer"
+export const prerendered = <h></h>;
+// @filename: othernoalias.tsx
+/** @jsx otherdom */
+import { otherdom } from "./renderer"
+export const prerendered2 = <h></h>;
+// @filename: reacty.tsx
+import React from "./renderer"
+export const prerendered3 = <h></h>;
+
+// @filename: index.tsx
+/** @jsx dom */
+import { dom } from "./renderer"
+<h></h>
+export * from "./other";
+export * from "./othernoalias";
+export * from "./reacty";
diff --git a/tests/cases/conformance/jsx/inline/inlineJsxFactoryOverridesCompilerOption.tsx b/tests/cases/conformance/jsx/inline/inlineJsxFactoryOverridesCompilerOption.tsx
new file mode 100644
index 0000000000000..4efd32876ad41
--- /dev/null
+++ b/tests/cases/conformance/jsx/inline/inlineJsxFactoryOverridesCompilerOption.tsx
@@ -0,0 +1,19 @@
+// @jsx: react
+// @jsxFactory: p
+// @filename: renderer.d.ts
+declare global {
+    namespace JSX {
+        interface IntrinsicElements {
+            [e: string]: any;
+        }
+    }
+}
+export function dom(): void;
+export { dom as p };
+// @filename: reacty.tsx
+/** @jsx dom */
+import {dom} from "./renderer";
+<h></h>
+// @filename: index.tsx
+import { p } from "./renderer";
+<h></h>
diff --git a/tests/cases/conformance/jsx/inline/inlineJsxFactoryWithFragmentIsError.tsx b/tests/cases/conformance/jsx/inline/inlineJsxFactoryWithFragmentIsError.tsx
new file mode 100644
index 0000000000000..280ac26b5ce1c
--- /dev/null
+++ b/tests/cases/conformance/jsx/inline/inlineJsxFactoryWithFragmentIsError.tsx
@@ -0,0 +1,19 @@
+// @jsx: react
+// @filename: renderer.d.ts
+declare global {
+    namespace JSX {
+        interface IntrinsicElements {
+            [e: string]: any;
+        }
+    }
+}
+export function dom(): void;
+export function createElement(): void;
+// @filename: reacty.tsx
+/** @jsx React.createElement */
+import * as React from "./renderer";
+<><h></h></>
+// @filename: index.tsx
+/** @jsx dom */
+import { dom } from "./renderer";
+<><h></h></>
\ No newline at end of file
diff --git a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter
index ed149eb0c787b..40bdb4eadabc9 160000
--- a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter
+++ b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter
@@ -1 +1 @@
-Subproject commit ed149eb0c787b1195a95b44105822c64bb6eb636
+Subproject commit 40bdb4eadabc9fbed7d83e3f26817a931c0763b6