import * as fs from 'fs' ; import * as path from 'path' ; import { TestHost } from "./testHost" ; export function getFilesByExt ( dir , ext ) { return fs . readdirSync ( dir ) . filter ( f => f [ 0 ] != "." ) . filter ( f => f . substr ( - ext . length ) === ext ) . map ( f => path . join ( dir , f ) ) ; } export const testAppTarget = { id : "core" , name : "Microsoft MakeCode" , title : "Microsoft MakeCode" , versions : undefined , description : "A toolkit to build JavaScript Blocks editors." , bundleddirs : [ ] , compile : { isNative : false , hasHex : false , jsRefCounting : true , switches : { } } , bundledpkgs : { } , appTheme : { } , tsprj : undefined , blocksprj : undefined , runtime : { pauseUntilBlock : { category : "Loops" , color : "0x0000ff" } , breakBlock : true , continueBlock : true , bannedCategories : [ "banned" ] } , corepkg : undefined } ; export interface PyConverterResult { python : string ; ts : string ; diagnostics : pxtc . KsDiagnostic [ ] ; } export function compareBaselines ( a , b , opts = { } ) { if ( ! opts . whitespaceSensitive ) { a = a . replace ( /\s/g , "" ) ; b = b . replace ( /\s/g , "" ) ; } return a === b ; } interface ComparisonOptions { whitespaceSensitive ? : boolean ; } export function getAndStripComparisonOptions ( input , isPython ) { const MARKER = isPython ? "#%" : "//%" ; if ( input . indexOf ( MARKER ) === 0 ) { const firstLineEnd = input . indexOf ( "\n" ) ; const optsString = input . substring ( MARKER . length , firstLineEnd ) ; const opts = JSON . parse ( optsString ) as ComparisonOptions ; const stripped = input . substr ( firstLineEnd + 1 ) ; return [ stripped , opts ] ; } return [ input , { } ] ; } export function replaceFileExtension ( file , extension ) { return file && file . substr ( 0 , file . length - path . extname ( file ) . length ) + extension ; } let cachedOpts = { } ; export function getTestCompileOptsAsync ( packageFiles = { [ pxt . MAIN_TS ] : "// no main" } , dependencies = [ ] , includeCommon = false ) { let cacheStr = Object . keys ( packageFiles ) . concat ( pxt . Util . values ( packageFiles ) ) . join ( "~" ) + dependencies . join ( "~" ) + includeCommon ; let cacheKey = pxt . Util . codalHash16 ( cacheStr ) ; if ( ! cachedOpts [ cacheKey ] ) { const pkg = new pxt . MainPackage ( new TestHost ( "test-pkg" , packageFiles , dependencies || [ ] , includeCommon ) ) ; const target = pkg . getTargetOptions ( ) ; target . isNative = false ; return pkg . getCompileOptionsAsync ( target ) . then ( opts => { opts . ast = true ; opts . testMode = true ; opts . ignoreFileResolutionErrors = true ; return cachedOpts [ cacheKey ] = opts ; } ) ; } let opts = JSON . parse ( JSON . stringify ( cachedOpts [ cacheKey ] ) ) ; return Promise . resolve ( opts ) ; } export function ts2pyAsync ( tsInput , dependency , includeCommon , caseName ) { const deps = [ ... ( dependency ? [ dependency ] : [ ] ) , ... ( ! includeCommon ? [ "bare" ] : [ ] ) ] ; return getTestCompileOptsAsync ( { [ pxt . MAIN_TS ] : tsInput } , deps , includeCommon ) . then ( opts => { let program = pxtc . getTSProgram ( opts ) ; const decompiled = pxt . py . decompileToPython ( program , pxt . MAIN_TS ) ; if ( decompiled . success ) { return decompiled . outfiles [ pxt . MAIN_PY ] ; } else { return Promise . reject ( new Error ( `Could not convert ts to py: ${ caseName }\n` + JSON . stringify ( decompiled . diagnostics , null , 4 ) ) ) ; } } ) ; } export function py2tsAsync ( pyInput , dependency , includeCommon , allowErrors , caseName ) { const deps = [ ... ( dependency ? [ dependency ] : [ ] ) , ... ( ! includeCommon ? [ "bare" ] : [ ] ) ] ; return getTestCompileOptsAsync ( { [ pxt . MAIN_PY ] : pyInput , [ pxt . MAIN_TS ] : "// no main" } , deps , includeCommon ) . then ( opts => { opts . target . preferredEditor = pxt . JAVASCRIPT_PROJECT_NAME ; let stsCompRes = pxtc . compile ( opts ) ; let apisInfo = pxtc . getApiInfo ( stsCompRes . ast , opts . jres ) ; if ( ! apisInfo || ! apisInfo . byQName ) throw Error ( "Failed to get apisInfo" ) ; opts . apisInfo = apisInfo ; opts . target . preferredEditor = pxt . PYTHON_PROJECT_NAME ; let { outfiles , diagnostics } = pxt . py . py2ts ( opts ) ; let success = diagnostics . length == 0 ; if ( success || allowErrors ) { return { python : pyInput , ts : opts . fileSystem [ pxt . MAIN_TS ] , diagnostics } ; } else { let partialOutput = outfiles [ pxt . MAIN_TS ] ; let errorStr = diagnostics . map ( pxtc . getDiagnosticString ) . join ( ) ; return Promise . reject ( new Error ( `Could not convert py to ts: ${ caseName }\n${ errorStr }\n${ partialOutput }` ) ) ; } } ) ; } export function stsAsync ( tsMain , dependency ? , includeCommon = false ) { const deps = [ ... ( dependency ? [ dependency ] : [ ] ) , ... ( ! includeCommon ? [ "bare" ] : [ ] ) ] ; return getTestCompileOptsAsync ( { [ pxt . MAIN_TS ] : tsMain } , deps , includeCommon ) . then ( opts => { const compiled = pxtc . compile ( opts ) ; if ( compiled . success ) { return compiled ; } else { let errStr = compiled . diagnostics . map ( pxtc . getDiagnosticString ) . join ( "" ) ; return Promise . reject ( new Error ( "Static Typescript could not compile:\n" + tsMain + "\nbecause:\n" + errStr ) ) ; } } ) ; }