2016-09-09 00:23:50 +02:00
namespace ts {
2016-09-07 16:04:46 +02:00
export function preProcessFile ( sourceText : string , readImportFiles = true , detectJavaScriptImports = false ) : PreProcessedFileInfo {
2018-02-27 01:10:00 +01:00
const pragmaContext : PragmaContext = {
2018-05-09 16:51:46 +02:00
languageVersion : ScriptTarget.ES5 , // controls whether the token scanner considers unicode identifiers or not - shouldn't matter, since we're only using it for trivia
2018-02-27 01:10:00 +01:00
pragmas : undefined ,
checkJsDirective : undefined ,
referencedFiles : [ ] ,
typeReferenceDirectives : [ ] ,
2018-05-03 20:00:10 +02:00
libReferenceDirectives : [ ] ,
2018-02-27 01:10:00 +01:00
amdDependencies : [ ] ,
hasNoDefaultLib : undefined ,
module Name : undefined
} ;
2016-09-07 16:04:46 +02:00
const importedFiles : FileReference [ ] = [ ] ;
2018-05-22 23:46:57 +02:00
let ambientExternalModules : { ref : FileReference , depth : number } [ ] | undefined ;
2018-04-15 03:27:51 +02:00
let lastToken : SyntaxKind ;
let currentToken : SyntaxKind ;
2016-09-07 16:04:46 +02:00
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
let externalModule = false ;
function nextToken() {
2018-04-16 20:48:04 +02:00
lastToken = currentToken ;
2018-04-15 03:27:51 +02:00
currentToken = scanner . scan ( ) ;
if ( currentToken === SyntaxKind . OpenBraceToken ) {
2016-09-07 16:04:46 +02:00
braceNesting ++ ;
}
2018-04-15 03:27:51 +02:00
else if ( currentToken === SyntaxKind . CloseBraceToken ) {
2016-09-07 16:04:46 +02:00
braceNesting -- ;
}
2018-04-15 03:27:51 +02:00
return currentToken ;
2016-09-07 16:04:46 +02:00
}
function getFileReference() {
2017-07-07 16:26:58 +02:00
const fileName = scanner . getTokenValue ( ) ;
2016-09-07 16:04:46 +02:00
const pos = scanner . getTokenPos ( ) ;
2017-07-07 16:26:58 +02:00
return { fileName , pos , end : pos + fileName . length } ;
2016-09-07 16:04:46 +02:00
}
function recordAmbientExternalModule ( ) : void {
if ( ! ambientExternalModules ) {
ambientExternalModules = [ ] ;
}
ambientExternalModules . push ( { ref : getFileReference ( ) , depth : braceNesting } ) ;
}
function recordModuleName() {
importedFiles . push ( getFileReference ( ) ) ;
markAsExternalModuleIfTopLevel ( ) ;
}
function markAsExternalModuleIfTopLevel() {
if ( braceNesting === 0 ) {
externalModule = true ;
}
}
/ * *
* Returns true if at least one token was consumed from the stream
* /
function tryConsumeDeclare ( ) : boolean {
let token = scanner . getToken ( ) ;
if ( token === SyntaxKind . DeclareKeyword ) {
// declare module "mod"
token = nextToken ( ) ;
if ( token === SyntaxKind . ModuleKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . StringLiteral ) {
recordAmbientExternalModule ( ) ;
}
}
return true ;
}
return false ;
}
/ * *
* Returns true if at least one token was consumed from the stream
* /
function tryConsumeImport ( ) : boolean {
2018-04-15 03:27:51 +02:00
if ( lastToken === SyntaxKind . DotToken ) {
return false ;
}
2016-09-07 16:04:46 +02:00
let token = scanner . getToken ( ) ;
2018-04-14 19:30:47 +02:00
if ( token === SyntaxKind . ImportKeyword ) {
2016-09-07 16:04:46 +02:00
token = nextToken ( ) ;
2017-06-08 20:59:31 +02:00
if ( token === SyntaxKind . OpenParenToken ) {
token = nextToken ( ) ;
2020-03-12 23:50:23 +01:00
if ( token === SyntaxKind . StringLiteral || token === SyntaxKind . NoSubstitutionTemplateLiteral ) {
2017-06-08 20:59:31 +02:00
// import("mod");
recordModuleName ( ) ;
return true ;
}
}
else if ( token === SyntaxKind . StringLiteral ) {
2016-09-07 16:04:46 +02:00
// import "mod";
recordModuleName ( ) ;
return true ;
}
else {
2020-03-23 18:11:01 +01:00
if ( token === SyntaxKind . TypeKeyword ) {
const skipTypeKeyword = scanner . lookAhead ( ( ) = > {
const token = scanner . scan ( ) ;
return token !== SyntaxKind . FromKeyword && (
token === SyntaxKind . AsteriskToken ||
token === SyntaxKind . OpenBraceToken ||
token === SyntaxKind . Identifier ||
isKeyword ( token )
) ;
} ) ;
if ( skipTypeKeyword ) {
token = nextToken ( ) ;
}
}
2016-09-07 16:04:46 +02:00
if ( token === SyntaxKind . Identifier || isKeyword ( token ) ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . FromKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . StringLiteral ) {
// import d from "mod";
recordModuleName ( ) ;
return true ;
}
}
else if ( token === SyntaxKind . EqualsToken ) {
if ( tryConsumeRequireCall ( /*skipCurrentToken*/ true ) ) {
return true ;
}
}
else if ( token === SyntaxKind . CommaToken ) {
// consume comma and keep going
token = nextToken ( ) ;
}
else {
// unknown syntax
return true ;
}
}
if ( token === SyntaxKind . OpenBraceToken ) {
token = nextToken ( ) ;
// consume "{ a as B, c, d as D}" clauses
// make sure that it stops on EOF
while ( token !== SyntaxKind . CloseBraceToken && token !== SyntaxKind . EndOfFileToken ) {
token = nextToken ( ) ;
}
if ( token === SyntaxKind . CloseBraceToken ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . FromKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . StringLiteral ) {
// import {a as A} from "mod";
// import d, {a, b as B} from "mod"
recordModuleName ( ) ;
}
}
}
}
else if ( token === SyntaxKind . AsteriskToken ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . AsKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . Identifier || isKeyword ( token ) ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . FromKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . StringLiteral ) {
// import * as NS from "mod"
// import d, * as NS from "mod"
recordModuleName ( ) ;
}
}
}
}
}
}
return true ;
}
return false ;
}
function tryConsumeExport ( ) : boolean {
let token = scanner . getToken ( ) ;
if ( token === SyntaxKind . ExportKeyword ) {
markAsExternalModuleIfTopLevel ( ) ;
token = nextToken ( ) ;
2020-03-23 18:11:01 +01:00
if ( token === SyntaxKind . TypeKeyword ) {
const skipTypeKeyword = scanner . lookAhead ( ( ) = > {
const token = scanner . scan ( ) ;
return token === SyntaxKind . AsteriskToken ||
token === SyntaxKind . OpenBraceToken ;
} ) ;
if ( skipTypeKeyword ) {
token = nextToken ( ) ;
}
}
2016-09-07 16:04:46 +02:00
if ( token === SyntaxKind . OpenBraceToken ) {
token = nextToken ( ) ;
// consume "{ a as B, c, d as D}" clauses
// make sure it stops on EOF
while ( token !== SyntaxKind . CloseBraceToken && token !== SyntaxKind . EndOfFileToken ) {
token = nextToken ( ) ;
}
if ( token === SyntaxKind . CloseBraceToken ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . FromKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . StringLiteral ) {
// export {a as A} from "mod";
// export {a, b as B} from "mod"
recordModuleName ( ) ;
}
}
}
}
else if ( token === SyntaxKind . AsteriskToken ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . FromKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . StringLiteral ) {
// export * from "mod"
recordModuleName ( ) ;
}
}
}
else if ( token === SyntaxKind . ImportKeyword ) {
token = nextToken ( ) ;
2020-03-23 18:11:01 +01:00
if ( token === SyntaxKind . TypeKeyword ) {
const skipTypeKeyword = scanner . lookAhead ( ( ) = > {
const token = scanner . scan ( ) ;
return token === SyntaxKind . Identifier ||
isKeyword ( token ) ;
} ) ;
if ( skipTypeKeyword ) {
token = nextToken ( ) ;
}
}
2016-09-07 16:04:46 +02:00
if ( token === SyntaxKind . Identifier || isKeyword ( token ) ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . EqualsToken ) {
if ( tryConsumeRequireCall ( /*skipCurrentToken*/ true ) ) {
return true ;
}
}
}
}
return true ;
}
return false ;
}
2020-03-12 23:50:23 +01:00
function tryConsumeRequireCall ( skipCurrentToken : boolean , allowTemplateLiterals = false ) : boolean {
2016-09-07 16:04:46 +02:00
let token = skipCurrentToken ? nextToken ( ) : scanner . getToken ( ) ;
if ( token === SyntaxKind . RequireKeyword ) {
token = nextToken ( ) ;
if ( token === SyntaxKind . OpenParenToken ) {
token = nextToken ( ) ;
2020-03-12 23:50:23 +01:00
if ( token === SyntaxKind . StringLiteral ||
allowTemplateLiterals && token === SyntaxKind . NoSubstitutionTemplateLiteral ) {
2016-09-07 16:04:46 +02:00
// require("mod");
recordModuleName ( ) ;
}
}
return true ;
}
return false ;
}
function tryConsumeDefine ( ) : boolean {
let token = scanner . getToken ( ) ;
if ( token === SyntaxKind . Identifier && scanner . getTokenValue ( ) === "define" ) {
token = nextToken ( ) ;
if ( token !== SyntaxKind . OpenParenToken ) {
return true ;
}
token = nextToken ( ) ;
2020-03-12 23:50:23 +01:00
if ( token === SyntaxKind . StringLiteral || token === SyntaxKind . NoSubstitutionTemplateLiteral ) {
2016-09-07 16:04:46 +02:00
// looks like define ("modname", ... - skip string literal and comma
token = nextToken ( ) ;
if ( token === SyntaxKind . CommaToken ) {
token = nextToken ( ) ;
}
else {
// unexpected token
return true ;
}
}
// should be start of dependency list
if ( token !== SyntaxKind . OpenBracketToken ) {
return true ;
}
// skip open bracket
token = nextToken ( ) ;
// scan until ']' or EOF
while ( token !== SyntaxKind . CloseBracketToken && token !== SyntaxKind . EndOfFileToken ) {
// record string literals as module names
2020-03-12 23:50:23 +01:00
if ( token === SyntaxKind . StringLiteral || token === SyntaxKind . NoSubstitutionTemplateLiteral ) {
2016-09-07 16:04:46 +02:00
recordModuleName ( ) ;
}
token = nextToken ( ) ;
}
return true ;
}
return false ;
}
function processImports ( ) : void {
scanner . setText ( sourceText ) ;
nextToken ( ) ;
// Look for:
// import "mod";
// import d from "mod"
// import {a as A } from "mod";
2018-01-08 17:52:13 +01:00
// import * as NS from "mod"
2016-09-07 16:04:46 +02:00
// import d, {a, b as B} from "mod"
// import i = require("mod");
2017-06-08 20:59:31 +02:00
// import("mod");
2016-09-07 16:04:46 +02:00
// export * from "mod"
// export {a as b} from "mod"
// export import i = require("mod")
// (for JavaScript files) require("mod")
2018-04-12 11:31:07 +02:00
// Do not look for:
// AnySymbol.import("mod")
2018-04-15 03:27:51 +02:00
// AnySymbol.nested.import("mod")
2018-04-12 11:31:07 +02:00
2016-09-07 16:04:46 +02:00
while ( true ) {
2018-04-15 03:30:37 +02:00
if ( scanner . getToken ( ) === SyntaxKind . EndOfFileToken ) {
2016-09-07 16:04:46 +02:00
break ;
}
// check if at least one of alternative have moved scanner forward
if ( tryConsumeDeclare ( ) ||
tryConsumeImport ( ) ||
tryConsumeExport ( ) ||
2020-03-12 23:50:23 +01:00
( detectJavaScriptImports && (
tryConsumeRequireCall ( /*skipCurrentToken*/ false , /*allowTemplateLiterals*/ true ) ||
tryConsumeDefine ( )
) ) ) {
2016-09-07 16:04:46 +02:00
continue ;
}
else {
nextToken ( ) ;
}
}
scanner . setText ( undefined ) ;
}
if ( readImportFiles ) {
processImports ( ) ;
}
2018-02-27 01:10:00 +01:00
processCommentPragmas ( pragmaContext , sourceText ) ;
processPragmasIntoFields ( pragmaContext , noop ) ;
2016-09-07 16:04:46 +02:00
if ( externalModule ) {
// for external modules module all nested ambient modules are augmentations
if ( ambientExternalModules ) {
// move all detected ambient modules to imported files since they need to be resolved
for ( const decl of ambientExternalModules ) {
importedFiles . push ( decl . ref ) ;
}
}
2018-05-29 22:46:32 +02:00
return { referencedFiles : pragmaContext.referencedFiles , typeReferenceDirectives : pragmaContext.typeReferenceDirectives , libReferenceDirectives : pragmaContext.libReferenceDirectives , importedFiles , isLibFile : ! ! pragmaContext . hasNoDefaultLib , ambientExternalModules : undefined } ;
2016-09-07 16:04:46 +02:00
}
else {
// for global scripts ambient modules still can have augmentations - look for ambient modules with depth > 0
2018-05-22 23:46:57 +02:00
let ambientModuleNames : string [ ] | undefined ;
2016-09-07 16:04:46 +02:00
if ( ambientExternalModules ) {
for ( const decl of ambientExternalModules ) {
if ( decl . depth === 0 ) {
if ( ! ambientModuleNames ) {
ambientModuleNames = [ ] ;
}
ambientModuleNames . push ( decl . ref . fileName ) ;
}
else {
importedFiles . push ( decl . ref ) ;
}
}
}
2018-05-29 22:46:32 +02:00
return { referencedFiles : pragmaContext.referencedFiles , typeReferenceDirectives : pragmaContext.typeReferenceDirectives , libReferenceDirectives : pragmaContext.libReferenceDirectives , importedFiles , isLibFile : ! ! pragmaContext . hasNoDefaultLib , ambientExternalModules : ambientModuleNames } ;
2016-09-07 16:04:46 +02:00
}
}
}