Some checks failed
		
		
	
	studiorailgun/Renderer/pipeline/head There was a failure building this commit
				
			
		
			
				
	
	
		
			480 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			480 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
 | 
						|
/**
 | 
						|
 * @description The compiler object
 | 
						|
 */
 | 
						|
let COMPILER = {
 | 
						|
 | 
						|
    //
 | 
						|
    //
 | 
						|
    //    VIRTUAL FILE SYSTEM
 | 
						|
    //
 | 
						|
    //
 | 
						|
 | 
						|
    /**
 | 
						|
     * The map of all source files to their content and compiled value
 | 
						|
     */
 | 
						|
    fileMap: { },
 | 
						|
 | 
						|
    /**
 | 
						|
     * The list of all source files to compile
 | 
						|
     */
 | 
						|
    sourceFiles: [ ],
 | 
						|
 | 
						|
    /**
 | 
						|
     * The top level directory, "/"
 | 
						|
     */
 | 
						|
    topLevelDirectory: {
 | 
						|
        //as required by our framework
 | 
						|
        Scripts: {
 | 
						|
            compiler: {
 | 
						|
                "host_access.js": {
 | 
						|
                    content: "",
 | 
						|
                    version: 0,
 | 
						|
                },
 | 
						|
                version: 0,
 | 
						|
                isDir: true,
 | 
						|
            },
 | 
						|
            version: 0,
 | 
						|
            isDir: true,
 | 
						|
        },
 | 
						|
        //as required by language service
 | 
						|
        node_modules: {
 | 
						|
            "@types": {
 | 
						|
                "lib.d.ts": {
 | 
						|
                    content: "",
 | 
						|
                    version: 0,
 | 
						|
                    isDir: false,
 | 
						|
                },
 | 
						|
                version: 0,
 | 
						|
                isDir: true,
 | 
						|
            },
 | 
						|
            version: 0,
 | 
						|
            isDir: true,
 | 
						|
        },
 | 
						|
        version: 0,
 | 
						|
        isDir: true,
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * The current directory, "/"
 | 
						|
     */
 | 
						|
    currentDirectory : { },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Registers a file with the compiler
 | 
						|
     * @param {string} fileName The file's name
 | 
						|
     * @param {string} content The content of the file
 | 
						|
     * @returns {string[]} The list of all files that still need to be registered by the host
 | 
						|
     */
 | 
						|
    registerFile: (fileName, content) => {
 | 
						|
 | 
						|
        //the list of files that are imported by this file
 | 
						|
        let dependentFiles = []
 | 
						|
    
 | 
						|
        loggerScripts.INFO('REGISTER FILE ' + fileName)
 | 
						|
        if(!COMPILER.fileMap[fileName]){
 | 
						|
            //create the virtual file
 | 
						|
            COMPILER.fileMap[fileName] = COMPILER.createFile(fileName,content)
 | 
						|
            //register the file itself
 | 
						|
            COMPILER.fileMap[fileName].tsSourceFile = ts.createSourceFile(
 | 
						|
                fileName,
 | 
						|
                content,
 | 
						|
                ts.ScriptTarget.Latest,
 | 
						|
            )
 | 
						|
            COMPILER.sourceFiles.push(fileName)
 | 
						|
            /**
 | 
						|
             * The preprocessed info about the file
 | 
						|
             * {
 | 
						|
             *   referencedFiles: ?,
 | 
						|
             *   typeReferenceDirectives: ?,
 | 
						|
             *   libReferenceDirectives: ?,
 | 
						|
             *   importedFiles: Array<{
 | 
						|
             *     fileName: string, //the path (without file ending) of the file that is imported by this file
 | 
						|
             *     pos: ?,
 | 
						|
             *     end: ?,
 | 
						|
             *   }>,
 | 
						|
             *   isLibFile: boolean,
 | 
						|
             *   ambientExternalModules: ?,
 | 
						|
             * }
 | 
						|
             */
 | 
						|
            const fileInfo = ts.preProcessFile(content)
 | 
						|
            loggerScripts.INFO('==========================')
 | 
						|
            loggerScripts.INFO(fileName)
 | 
						|
            loggerScripts.INFO('Registered file depends on:')
 | 
						|
            fileInfo.importedFiles.forEach(module => {
 | 
						|
                let extension = ".ts"
 | 
						|
                /**
 | 
						|
                 * {
 | 
						|
                 *   resolvedModule: ?,
 | 
						|
                 *   failedLookupLocations: Array<string>,
 | 
						|
                 *   affectingLocations: ?,
 | 
						|
                 *   resolutionDiagnostics: ?,
 | 
						|
                 *   alternateResult: ?,
 | 
						|
                 * }
 | 
						|
                 */
 | 
						|
                const resolvedImport = ts.resolveModuleName(module.fileName,fileName,COMPILER.compilerOptions,COMPILER.customCompilerHost)
 | 
						|
                if(resolvedImport?.resolvedModule){
 | 
						|
                    /**
 | 
						|
                     * undefined
 | 
						|
                     * OR
 | 
						|
                     * {
 | 
						|
                     *   resolvedFileName: ?,
 | 
						|
                     *   originalPath: ?,
 | 
						|
                     *   extension: string, (ie ".js", ".ts", etc)
 | 
						|
                     *   isExternalLibraryImport: boolean,
 | 
						|
                     *   packageId: ?,
 | 
						|
                     *   resolvedUsingTsExtension: boolean,
 | 
						|
                     * }
 | 
						|
                     */
 | 
						|
                    const module = resolvedImport.resolvedModule
 | 
						|
                    extension = module.extension
 | 
						|
                }
 | 
						|
                //am assuming we're always importing typescript for the time being
 | 
						|
                const dependentFile = module.fileName + extension
 | 
						|
                const normalizedDependentFilePath = FILE_RESOLUTION_getFilePath(dependentFile,false)
 | 
						|
                if(!COMPILER.fileMap[normalizedDependentFilePath]){
 | 
						|
                    dependentFiles.push(normalizedDependentFilePath)
 | 
						|
                    loggerScripts.INFO(" - " + normalizedDependentFilePath)
 | 
						|
                }
 | 
						|
            })
 | 
						|
    
 | 
						|
            //If the compiler has already run once, run the language service against only this file
 | 
						|
            if(!!COMPILER.compilerHasRun){
 | 
						|
                COMPILER.emitFile(fileName)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return dependentFiles;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates a file object for a given path
 | 
						|
     * @param string} fileName The name of the file
 | 
						|
     * @param {string} content The content of the file
 | 
						|
     * @returns The file object
 | 
						|
     */
 | 
						|
    createFile: (fileName, content) => {
 | 
						|
        //get the file path array
 | 
						|
        const filePathArray = COMPILER.getPath(fileName)
 | 
						|
        let mutableArray = filePathArray
 | 
						|
    
 | 
						|
        //the current folder as we recursively create folders to populate this file
 | 
						|
        let currentFolder = COMPILER.topLevelDirectory
 | 
						|
    
 | 
						|
        //recursively create directories until our file is written
 | 
						|
        while(mutableArray.length > 1){
 | 
						|
            let nextDirName = mutableArray.shift()
 | 
						|
            if(!currentFolder?.[nextDirName]){
 | 
						|
                //create directory
 | 
						|
                currentFolder[nextDirName] = {
 | 
						|
                    isDir: true,
 | 
						|
                    "..": currentFolder,
 | 
						|
                }
 | 
						|
            }
 | 
						|
            currentFolder = currentFolder?.[nextDirName]
 | 
						|
        }
 | 
						|
    
 | 
						|
        //create the actual file
 | 
						|
        currentFolder[mutableArray[0]] = {
 | 
						|
            isDir: false,
 | 
						|
            dir: currentFolder,
 | 
						|
            content: content,
 | 
						|
            version: 0,
 | 
						|
        }
 | 
						|
    
 | 
						|
        //return the file
 | 
						|
        return currentFolder[mutableArray[0]]
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Gets the path for the file
 | 
						|
     * @param {string} fullyQualifiedFilePath The fully qualified file path
 | 
						|
     * @returns {string[]} The array of directories ending with the name of the file
 | 
						|
     */
 | 
						|
    getPath: (fullyQualifiedFilePath) => {
 | 
						|
        let modifiedFileName = fullyQualifiedFilePath
 | 
						|
        //remove leading "/"
 | 
						|
        if(modifiedFileName.startsWith("/")){
 | 
						|
            modifiedFileName = modifiedFileName.substring(1)
 | 
						|
        }
 | 
						|
        //split
 | 
						|
        return modifiedFileName.split("/")
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Gets the path for the file
 | 
						|
     * @param {stringp[]} filePathArray The fully qualified file path
 | 
						|
     * @returns The array of directories ending with the name of the file
 | 
						|
     */
 | 
						|
    getFileByPath: (filePathArray) => {
 | 
						|
        let currentFolder = COMPILER.topLevelDirectory
 | 
						|
        let mutableArray = filePathArray
 | 
						|
    
 | 
						|
        //illegal state
 | 
						|
        if(mutableArray?.length < 1){
 | 
						|
            throw new Error("Trying to get a file with a path array of length 0!")
 | 
						|
        }
 | 
						|
    
 | 
						|
        while(mutableArray?.length > 1){
 | 
						|
            let nextDirName = mutableArray.shift()
 | 
						|
            currentFolder = currentFolder?.[nextDirName]
 | 
						|
            if(!currentFolder){
 | 
						|
                let errorMessage = "Trying to get file in directory that doesn't exist! \n" +
 | 
						|
                nextDirName
 | 
						|
                throw new Error(errorMessage)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return currentFolder[mutableArray?.[0]]
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Checks if a file exists
 | 
						|
     * @param {string[]} filePathArray The file path array
 | 
						|
     * @returns true if it exists, false otherwise
 | 
						|
     */
 | 
						|
    fileExists: (filePathArray) => {
 | 
						|
        let currentFolder = COMPILER.topLevelDirectory
 | 
						|
        let mutableArray = filePathArray
 | 
						|
    
 | 
						|
        //illegal state
 | 
						|
        if(mutableArray?.length < 1){
 | 
						|
            throw new Error("Trying to get a file with a path array of length 0!")
 | 
						|
        }
 | 
						|
    
 | 
						|
        while(mutableArray.length > 1){
 | 
						|
            let nextDirName = mutableArray.shift()
 | 
						|
            currentFolder = currentFolder?.[nextDirName]
 | 
						|
            if(!currentFolder){
 | 
						|
                return false
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return !!currentFolder?.[mutableArray[0]]
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * The callback invoked when the compiler host tries to read a file
 | 
						|
     * @param {string} fileName The name of the file
 | 
						|
     * @param {*} languageVersion The language version
 | 
						|
     * @returns The file if it exists, null otherwise
 | 
						|
     */
 | 
						|
    getSourceFile: (fileName, languageVersion) => {
 | 
						|
        if(!!COMPILER.fileMap[fileName]){
 | 
						|
            return COMPILER.fileMap[fileName].tsSourceFile
 | 
						|
        } else {
 | 
						|
            return null
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
 | 
						|
    //
 | 
						|
    //
 | 
						|
    //   COMPILATION
 | 
						|
    //
 | 
						|
    //
 | 
						|
 | 
						|
    /**
 | 
						|
     * The compiler options
 | 
						|
     */
 | 
						|
    compilerOptions: { },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Tracks whether the compiler has run or not
 | 
						|
     */
 | 
						|
    compilerHasRun: false,
 | 
						|
 | 
						|
    /**
 | 
						|
     * The typescript compiler host definition
 | 
						|
     */
 | 
						|
    customCompilerHost: null,
 | 
						|
 | 
						|
    /**
 | 
						|
     * The typescript program
 | 
						|
     */
 | 
						|
    program: null,
 | 
						|
 | 
						|
    /**
 | 
						|
     * Emits a file
 | 
						|
     * @param {string} fileName The name of the file
 | 
						|
     * @returns {void}
 | 
						|
     */
 | 
						|
    emitFile: (fileName) => {
 | 
						|
        loggerScripts.DEBUG('Compiler evaluating source path ' + fileName)
 | 
						|
        /**
 | 
						|
         * {
 | 
						|
         *   outputFiles: [ ],
 | 
						|
         *   emitSkipped: boolean,
 | 
						|
         *   diagnostics: { },
 | 
						|
         * }
 | 
						|
         */
 | 
						|
        const output = COMPILER.program.getEmitOutput(fileName)
 | 
						|
        if (!output.emitSkipped) {
 | 
						|
            output.outputFiles.forEach(outputFile => {
 | 
						|
                loggerScripts.DEBUG(`[ts] Emitting ${outputFile}`);
 | 
						|
                COMPILER.customCompilerHost.writeFile(outputFile.name, outputFile.text)
 | 
						|
            })
 | 
						|
        } else {
 | 
						|
            loggerScripts.DEBUG(`[ts] Emitting ${fileName} failed`);
 | 
						|
            COMPILER.logEmitError(fileName);
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Logs errors raised during emission of files
 | 
						|
     * @param {string} fileName The name of the file to log errors about
 | 
						|
     * @returns {void}
 | 
						|
     */
 | 
						|
    logEmitError: (fileName) => {
 | 
						|
        loggerScripts.DEBUG('[ts] logErrors ' + fileName)
 | 
						|
        let allDiagnostics = services
 | 
						|
            .getCompilerOptionsDiagnostics()
 | 
						|
            .concat(services.getSyntacticDiagnostics(fileName))
 | 
						|
            .concat(services.getSemanticDiagnostics(fileName));
 | 
						|
    
 | 
						|
        allDiagnostics.forEach(diagnostic => {
 | 
						|
            let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
 | 
						|
            if (diagnostic.file) {
 | 
						|
                let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
 | 
						|
                    diagnostic.start
 | 
						|
                );
 | 
						|
                loggerScripts.DEBUG(`[ts]  Error ${diagnostic.file.fileName} (${line + 1},${character +1}): ${message}`);
 | 
						|
            } else {
 | 
						|
                loggerScripts.DEBUG(`[ts]  Error: ${message}`);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Instructs Typescript to emit the final compiled value
 | 
						|
     */
 | 
						|
    run: () => {
 | 
						|
        loggerScripts.INFO('COMPILE ALL REGISTERED FILES')
 | 
						|
    
 | 
						|
        if(!COMPILER.program){
 | 
						|
            COMPILER.program = ts.createLanguageService(COMPILER.customCompilerHost, ts.createDocumentRegistry());
 | 
						|
        }
 | 
						|
    
 | 
						|
        //Emit all currently known files
 | 
						|
        COMPILER.sourceFiles.forEach(sourcePath => {
 | 
						|
            COMPILER.emitFile(sourcePath)
 | 
						|
        })
 | 
						|
    
 | 
						|
        //flag that the compiler has run (ie only incrementally compile when new files are added, now)
 | 
						|
        COMPILER.compilerHasRun = true
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Loads a file
 | 
						|
     * @param {*} fileName The name of the file to load (preferably already has .ts at the end)
 | 
						|
     */
 | 
						|
    runFile: (fileName) => {
 | 
						|
        let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
 | 
						|
        if(!!COMPILER.fileMap[normalizedFilePath]){
 | 
						|
            loggerScripts.INFO('RUN FILE ' + normalizedFilePath)
 | 
						|
            eval(COMPILER.fileMap[normalizedFilePath].content)
 | 
						|
        } else {
 | 
						|
            const message = 'FAILED TO RESOLVE FILE ' + normalizedFilePath
 | 
						|
            loggerScripts.WARNING(message)
 | 
						|
            throw new Error(message)
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Loads a file
 | 
						|
     * @param {*} fileName The name of the file to load (preferably already has .ts at the end)
 | 
						|
     */
 | 
						|
    printSource: (fileName) => {
 | 
						|
        let normalizedFilePath = FILE_RESOLUTION_getFilePath(fileName)
 | 
						|
        if(!!COMPILER.fileMap[normalizedFilePath]){
 | 
						|
            loggerScripts.INFO('FILE CONTENT ' + normalizedFilePath)
 | 
						|
        } else {
 | 
						|
            const message = 'FAILED TO RESOLVE FILE ' + normalizedFilePath
 | 
						|
            loggerScripts.WARNING(message)
 | 
						|
            loggerScripts.WARNING('file map content:')
 | 
						|
            loggerScripts.WARNING(OBject.keys(COMPILER.fileMap) + "")
 | 
						|
            throw new Error(message)
 | 
						|
        }
 | 
						|
    },
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Constructs the compiler host
 | 
						|
 * https://www.typescriptlang.org/tsconfig/#compilerOptions
 | 
						|
 * 
 | 
						|
 * Examples:
 | 
						|
 * https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
 | 
						|
 * 
 | 
						|
 */
 | 
						|
COMPILER.customCompilerHost = {
 | 
						|
    getSourceFile: COMPILER.getSourceFile,
 | 
						|
    writeFile: (fileName, data) => {
 | 
						|
        loggerScripts.INFO("EMIT FILE " + fileName)
 | 
						|
        //wrap in require logic
 | 
						|
        let finalData = 
 | 
						|
        "let exports = { }\n" +
 | 
						|
        data + "\n" +
 | 
						|
        "return exports"
 | 
						|
 | 
						|
        //create file
 | 
						|
        COMPILER.createFile(fileName,finalData)
 | 
						|
        
 | 
						|
        //register in file map
 | 
						|
        COMPILER.fileMap[fileName] = {
 | 
						|
            content: data, //to be eval'd from top level
 | 
						|
            moduleContent: finalData, //to be eval'd from require()
 | 
						|
        }
 | 
						|
    },
 | 
						|
    getDefaultLibFileName: ts.getDefaultLibFileName,
 | 
						|
    useCaseSensitiveFileNames: () => false,
 | 
						|
    getCanonicalFileName: filename => filename,
 | 
						|
    getCurrentDirectory: () => "/",
 | 
						|
    getNewLine: () => "\n",
 | 
						|
    getDirectories: (path) => {
 | 
						|
        loggerScripts.DEBUG('[ts] getDirectories ' + path)
 | 
						|
        const dirs = Object.keys(COMPILER.getFileByPath(COMPILER.getPath(path)))
 | 
						|
        loggerScripts.DEBUG('[ts] dirs: ' + dirs)
 | 
						|
        return dirs
 | 
						|
    },
 | 
						|
    directoryExists: (path) => {
 | 
						|
        let exists = COMPILER.fileExists(COMPILER.getPath(path))
 | 
						|
        if(exists){
 | 
						|
            exists = COMPILER.getFileByPath(COMPILER.getPath(path))?.isDir
 | 
						|
        }
 | 
						|
        loggerScripts.DEBUG('[ts] directoryExists ' + path + " - " + exists)
 | 
						|
        return false
 | 
						|
    },
 | 
						|
    fileExists: (path) => {
 | 
						|
        const exists = COMPILER.fileExists(COMPILER.getPath(path))
 | 
						|
        loggerScripts.DEBUG('[ts] fileExists ' + path + " - " + exists)
 | 
						|
        return exists
 | 
						|
    },
 | 
						|
    readFile: (path) => {
 | 
						|
        loggerScripts.DEBUG('[ts] readFile ' + path)
 | 
						|
        const file = COMPILER.getFileByPath(COMPILER.getPath(path))
 | 
						|
        loggerScripts.DEBUG('[ts] readFile (content): ' + file.content)
 | 
						|
        return file.content
 | 
						|
    },
 | 
						|
    getScriptFileNames: () => {
 | 
						|
        loggerScripts.DEBUG('[ts] getScriptFileNames')
 | 
						|
        return COMPILER.sourceFiles
 | 
						|
    },
 | 
						|
    getScriptVersion: (fileName) => {
 | 
						|
        loggerScripts.DEBUG('[ts] getScriptVersion: ' + fileName)
 | 
						|
        const file = COMPILER.getFileByPath(COMPILER.getPath(fileName))
 | 
						|
        return file?.version
 | 
						|
    },
 | 
						|
    //https://github.com/microsoft/TypeScript/wiki/Using-the-Language-Service-API#scriptsnapshot
 | 
						|
    getScriptSnapshot: (fileName) => {
 | 
						|
        loggerScripts.DEBUG('[ts] getScriptSnapshot: ' + fileName)
 | 
						|
        const file = COMPILER.getFileByPath(COMPILER.getPath(fileName))
 | 
						|
        if(file){
 | 
						|
            return ts.ScriptSnapshot.fromString(file.content)
 | 
						|
        } else {
 | 
						|
            return undefined
 | 
						|
        }
 | 
						|
    },
 | 
						|
    getCompilationSettings: () => COMPILER.compilerOptions,
 | 
						|
}
 | 
						|
 | 
						|
//initialized CWD
 | 
						|
COMPILER.currentDirectory = COMPILER.topLevelDirectory
 |