From 32c63a26284c0fcf6dfbdce709f3da680c2940d9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 26 Feb 2018 16:10:00 -0800 Subject: [PATCH] Add support for transpiling per-file jsx pragmas (#21218) * Add support for per-file jsx pragmas * Add error for using jsx factory pragma with fragments * More tests, use different regex class for pragma capture * Unify all pragma parsing machinery --- src/compiler/checker.ts | 30 +- src/compiler/diagnosticMessages.json | 4 + src/compiler/factory.ts | 3 + src/compiler/parser.ts | 317 +++++++++++++----- src/compiler/transformers/jsx.ts | 4 +- src/compiler/types.ts | 130 ++++++- src/compiler/utilities.ts | 34 -- src/services/codefixes/importFixes.ts | 2 +- src/services/preProcess.ts | 39 +-- src/services/services.ts | 3 + .../reference/inlineJsxFactoryDeclarations.js | 74 ++++ .../inlineJsxFactoryDeclarations.symbols | 80 +++++ .../inlineJsxFactoryDeclarations.types | 85 +++++ ...inlineJsxFactoryOverridesCompilerOption.js | 32 ++ ...eJsxFactoryOverridesCompilerOption.symbols | 39 +++ ...ineJsxFactoryOverridesCompilerOption.types | 41 +++ ...neJsxFactoryWithFragmentIsError.errors.txt | 26 ++ .../inlineJsxFactoryWithFragmentIsError.js | 35 ++ ...nlineJsxFactoryWithFragmentIsError.symbols | 39 +++ .../inlineJsxFactoryWithFragmentIsError.types | 43 +++ .../inline/inlineJsxFactoryDeclarations.tsx | 36 ++ ...nlineJsxFactoryOverridesCompilerOption.tsx | 19 ++ .../inlineJsxFactoryWithFragmentIsError.tsx | 19 ++ 23 files changed, 975 insertions(+), 159 deletions(-) create mode 100644 tests/baselines/reference/inlineJsxFactoryDeclarations.js create mode 100644 tests/baselines/reference/inlineJsxFactoryDeclarations.symbols create mode 100644 tests/baselines/reference/inlineJsxFactoryDeclarations.types create mode 100644 tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.js create mode 100644 tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.symbols create mode 100644 tests/baselines/reference/inlineJsxFactoryOverridesCompilerOption.types create mode 100644 tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.errors.txt create mode 100644 tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.js create mode 100644 tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.symbols create mode 100644 tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.types create mode 100644 tests/cases/conformance/jsx/inline/inlineJsxFactoryDeclarations.tsx create mode 100644 tests/cases/conformance/jsx/inline/inlineJsxFactoryOverridesCompilerOption.tsx create mode 100644 tests/cases/conformance/jsx/inline/inlineJsxFactoryWithFragmentIsError.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2fcc924a7b8af..63b8d18c451f6 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 ? (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 9801da54a5ff5..23549f012a00f 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 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(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: 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* 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: 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(); + 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, 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 6b325bf6abe11..6631c66f0eb3c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2568,6 +2568,9 @@ namespace ts { /* @internal */ ambientModuleNames: ReadonlyArray; /* @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): 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 { @@ -5127,4 +5130,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 + * /// + */ + 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 { + 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 { + args?: [PragmaArgumentSpecification] | [PragmaArgumentSpecification, PragmaArgumentSpecification] | [PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification]; + // 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}, 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 extends {captureSpan: true} ? {value: string, pos: number, end: number} : string; + + /* @internal */ + type PragmaArgTypeOptional = + TDesc extends {optional: true} + ? {[K in TName]?: PragmaArgTypeMaybeCapture} + : {[K in TName]: PragmaArgTypeMaybeCapture}; + + /** + * 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 { args: [PragmaArgumentSpecification, PragmaArgumentSpecification, PragmaArgumentSpecification] } + ? PragmaArgTypeOptional & PragmaArgTypeOptional & PragmaArgTypeOptional + : T extends { args: [PragmaArgumentSpecification, PragmaArgumentSpecification] } + ? PragmaArgTypeOptional & PragmaArgTypeOptional + : T extends { args: [PragmaArgumentSpecification] } + ? PragmaArgTypeOptional + : 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, 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 { + set(key: TKey, value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][]): this; + get(key: TKey): PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][]; + forEach(action: (value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][], key: TKey) => void): void; + } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index af0dc443e92a4..63c8d6989f4cd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1989,40 +1989,6 @@ namespace ts { return undefined; } - export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult { - const simpleReferenceRegEx = /^\/\/\/\s*parent).tagName === token) || parent.kind === SyntaxKind.JsxOpeningFragment) { - umdSymbol = checker.resolveName(checker.getJsxNamespace(), + umdSymbol = checker.resolveName(checker.getJsxNamespace(parent), isNodeOpeningLikeElement ? (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 624ca8c42bb27..a4da9b12eadd7 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"; + +//// [other.tsx] +/** @jsx h */ +import { dom as h } from "./renderer" +export const prerendered = ; +//// [othernoalias.tsx] +/** @jsx otherdom */ +import { otherdom } from "./renderer" +export const prerendered2 = ; +//// [reacty.tsx] +import React from "./renderer" +export const prerendered3 = ; + +//// [index.tsx] +/** @jsx dom */ +import { dom } from "./renderer" + +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 : 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 = ; +>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 = ; +>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 = ; +>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 : 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 + + +> : 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 = ; +>prerendered : any +> : any +>h : () => void +>h : () => void + +=== tests/cases/conformance/jsx/inline/othernoalias.tsx === +/** @jsx otherdom */ +import { otherdom } from "./renderer" +>otherdom : () => void + +export const prerendered2 = ; +>prerendered2 : any +> : any +>h : any +>h : any + +=== tests/cases/conformance/jsx/inline/reacty.tsx === +import React from "./renderer" +>React : () => void + +export const prerendered3 = ; +>prerendered3 : any +> : any +>h : any +>h : any + +=== tests/cases/conformance/jsx/inline/index.tsx === +/** @jsx dom */ +import { dom } from "./renderer" +>dom : () => void + + +> : 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"; + +//// [index.tsx] +import { p } from "./renderer"; + + + +//// [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 : 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 : 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 + + +> : any +>h : any +>h : any + +=== tests/cases/conformance/jsx/inline/index.tsx === +import { p } from "./renderer"; +>p : () => void + + +> : 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"; + <> + ~~~~~~~~~~~~ +!!! 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"; + <> + ~~~~~~~~~~~~ +!!! 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"; +<> +//// [index.tsx] +/** @jsx dom */ +import { dom } from "./renderer"; +<> + +//// [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 : 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 : 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 + +<> +><> : any +> : any +>h : any +>h : any + +=== tests/cases/conformance/jsx/inline/index.tsx === +/** @jsx dom */ +import { dom } from "./renderer"; +>dom : () => void + +<> +><> : any +> : 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"; + +// @filename: other.tsx +/** @jsx h */ +import { dom as h } from "./renderer" +export const prerendered = ; +// @filename: othernoalias.tsx +/** @jsx otherdom */ +import { otherdom } from "./renderer" +export const prerendered2 = ; +// @filename: reacty.tsx +import React from "./renderer" +export const prerendered3 = ; + +// @filename: index.tsx +/** @jsx dom */ +import { dom } from "./renderer" + +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"; + +// @filename: index.tsx +import { p } from "./renderer"; + 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"; +<> +// @filename: index.tsx +/** @jsx dom */ +import { dom } from "./renderer"; +<> \ No newline at end of file