1// @ts-check 2 3// This script does two things: 4// 5// - Listens to changes to the built version of TypeScript (via a filewatcher on `built/local/typescriptServices.js`) 6// these trigger creating monaco-typescript compatible builds of TypeScript at `internal/lib/typescriptServices.js§ 7// 8// - Creates a HTTP server which the playground uses. The webserver almost exclusively re-directs requests to 9// the latest stable version of monaco-typescript, but specifically overrides requests for the TypeScript js 10// file to the version created in the above step. 11// 12 13/*--------------------------------------------------------------------------------------------- 14 * Copyright (c) Microsoft Corporation. All rights reserved. 15 * Licensed under the MIT License. See License.txt in the project root for license information. 16 *--------------------------------------------------------------------------------------------*/ 17 18const path = require('path'); 19const fs = require('fs'); 20const child_process = require('child_process'); 21const http = require('http'); 22const url = require('url'); 23 24function updateTSDist() { 25 // This code is a direct port of a script from monaco-typescript 26 // https://github.com/microsoft/monaco-typescript/blob/master/scripts/importTypescript.js 27 // Currently based on 778ace1 on Apr 25 2020 28 29 const generatedNote = `// 30 // **NOTE**: Do not edit directly! This file is generated using \`npm run import-typescript\` 31 // 32 `; 33 34 const TYPESCRIPT_LIB_SOURCE = path.join(__dirname, '../built/local'); 35 const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../internal/lib'); 36 37 (function () { 38 try { 39 fs.statSync(TYPESCRIPT_LIB_DESTINATION); 40 } catch (err) { 41 fs.mkdirSync(TYPESCRIPT_LIB_DESTINATION); 42 } 43 importLibs(); 44 45 const npmLsOutput = JSON.parse(child_process.execSync("npm ls typescript --depth=0 --json=true").toString()); 46 const typeScriptDependencyVersion = npmLsOutput.dependencies.typescript.version; 47 48 fs.writeFileSync( 49 path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServicesMetadata.ts'), 50 `${generatedNote} 51 export const typescriptVersion = "${typeScriptDependencyVersion}";\n` 52 ); 53 54 var tsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js')).toString(); 55 56 // Ensure we never run into the node system... 57 // (this also removes require calls that trick webpack into shimming those modules...) 58 tsServices = ( 59 tsServices.replace(/\n ts\.sys =([^]*)\n \}\)\(\);/m, `\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE`) 60 ); 61 62 // Eliminate more require() calls... 63 tsServices = tsServices.replace(/^( +)etwModule = require\(.*$/m, '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE'); 64 tsServices = tsServices.replace(/^( +)var result = ts\.sys\.require\(.*$/m, '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE'); 65 66 // Flag any new require calls (outside comments) so they can be corrected preemptively. 67 // To avoid missing cases (or using an even more complex regex), temporarily remove comments 68 // about require() and then check for lines actually calling require(). 69 // \/[*/] matches the start of a comment (single or multi-line). 70 // ^\s+\*[^/] matches (presumably) a later line of a multi-line comment. 71 const tsServicesNoCommentedRequire = tsServices.replace(/(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm, ''); 72 const linesWithRequire = tsServicesNoCommentedRequire.match(/^.*?\brequire\(.*$/gm) 73 74 // Allow error messages to include references to require() in their strings 75 const runtimeRequires = linesWithRequire && linesWithRequire.filter(l => !l.includes(": diag(")) 76 77 if (runtimeRequires && runtimeRequires.length) { 78 console.error('Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n'); 79 console.error(linesWithRequire.join('\n')); 80 process.exit(1); 81 } 82 83 // Make sure process.args don't get called in the browser, this 84 // should only happen in TS 2.6.2 85 const beforeProcess = `ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify(process.argv));` 86 const afterProcess = `// MONACOCHANGE\n ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify([]));\n// END MONACOCHANGE` 87 tsServices = tsServices.replace(beforeProcess, afterProcess); 88 89 var tsServices_amd = generatedNote + tsServices + 90 ` 91 // MONACOCHANGE 92 // Defining the entire module name because r.js has an issue and cannot bundle this file 93 // correctly with an anonymous define call 94 define("vs/language/typescript/lib/typescriptServices", [], function() { return ts; }); 95 // END MONACOCHANGE 96 `; 97 fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices-amd.js'), tsServices_amd); 98 99 var tsServices_esm = generatedNote + tsServices + 100 ` 101 // MONACOCHANGE 102 export var createClassifier = ts.createClassifier; 103 export var createLanguageService = ts.createLanguageService; 104 export var displayPartsToString = ts.displayPartsToString; 105 export var EndOfLineState = ts.EndOfLineState; 106 export var flattenDiagnosticMessageText = ts.flattenDiagnosticMessageText; 107 export var IndentStyle = ts.IndentStyle; 108 export var ScriptKind = ts.ScriptKind; 109 export var ScriptTarget = ts.ScriptTarget; 110 export var TokenClass = ts.TokenClass; 111 // END MONACOCHANGE 112 `; 113 fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.js'), tsServices_esm); 114 115 var dtsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.d.ts')).toString(); 116 dtsServices += 117 ` 118 // MONACOCHANGE 119 export = ts; 120 // END MONACOCHANGE 121 `; 122 fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'), generatedNote + dtsServices); 123 124 })(); 125 126 function importLibs() { 127 function getFileName(name) { 128 return (name === '' ? 'lib.d.ts' : `lib.${name}.d.ts`); 129 } 130 function getVariableName(name) { 131 return (name === '' ? 'lib_dts' : `lib_${name.replace(/\./g, '_')}_dts`); 132 } 133 function readLibFile(name) { 134 var srcPath = path.join(TYPESCRIPT_LIB_SOURCE, getFileName(name)); 135 return fs.readFileSync(srcPath).toString(); 136 } 137 138 var queue = []; 139 var in_queue = {}; 140 141 var enqueue = function (name) { 142 if (in_queue[name]) { 143 return; 144 } 145 in_queue[name] = true; 146 queue.push(name); 147 }; 148 149 enqueue(''); 150 enqueue('es2015'); 151 152 var result = []; 153 while (queue.length > 0) { 154 var name = queue.shift(); 155 var contents = readLibFile(name); 156 var lines = contents.split(/\r\n|\r|\n/); 157 158 var output = ''; 159 var writeOutput = function (text) { 160 if (output.length === 0) { 161 output = text; 162 } else { 163 output += ` + ${text}`; 164 } 165 }; 166 var outputLines = []; 167 var flushOutputLines = function () { 168 writeOutput(`"${escapeText(outputLines.join('\n'))}"`); 169 outputLines = []; 170 }; 171 var deps = []; 172 for (let i = 0; i < lines.length; i++) { 173 let m = lines[i].match(/\/\/\/\s*<reference\s*lib="([^"]+)"/); 174 if (m) { 175 flushOutputLines(); 176 writeOutput(getVariableName(m[1])); 177 deps.push(getVariableName(m[1])); 178 enqueue(m[1]); 179 continue; 180 } 181 outputLines.push(lines[i]); 182 } 183 flushOutputLines(); 184 185 result.push({ 186 name: getVariableName(name), 187 deps: deps, 188 output: output 189 }); 190 } 191 192 var strResult = `/*--------------------------------------------------------------------------------------------- 193 * Copyright (c) Microsoft Corporation. All rights reserved. 194 * Licensed under the MIT License. See License.txt in the project root for license information. 195 *--------------------------------------------------------------------------------------------*/ 196 ${generatedNote}`; 197 // Do a topological sort 198 while (result.length > 0) { 199 for (let i = result.length - 1; i >= 0; i--) { 200 if (result[i].deps.length === 0) { 201 // emit this node 202 strResult += `\nexport const ${result[i].name}: string = ${result[i].output};\n`; 203 204 // mark dep as resolved 205 for (let j = 0; j < result.length; j++) { 206 for (let k = 0; k < result[j].deps.length; k++) { 207 if (result[j].deps[k] === result[i].name) { 208 result[j].deps.splice(k, 1); 209 break; 210 } 211 } 212 } 213 214 // remove from result 215 result.splice(i, 1); 216 break; 217 } 218 } 219 } 220 221 strResult += ` 222 /** This is the DTS which is used when the target is ES6 or below */ 223 export const lib_es5_bundled_dts = lib_dts; 224 225 /** This is the DTS which is used by default in monaco-typescript, and when the target is 2015 or above */ 226 export const lib_es2015_bundled_dts = lib_es2015_dts + "" + lib_dom_dts + "" + lib_webworker_importscripts_dts + "" + lib_scripthost_dts + ""; 227 ` 228 229 var dstPath = path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.ts'); 230 fs.writeFileSync(dstPath, strResult); 231 } 232 233 /** 234 * Escape text such that it can be used in a javascript string enclosed by double quotes (") 235 */ 236 function escapeText(text) { 237 // See http://www.javascriptkit.com/jsref/escapesequence.shtml 238 var _backspace = '\b'.charCodeAt(0); 239 var _formFeed = '\f'.charCodeAt(0); 240 var _newLine = '\n'.charCodeAt(0); 241 var _nullChar = 0; 242 var _carriageReturn = '\r'.charCodeAt(0); 243 var _tab = '\t'.charCodeAt(0); 244 var _verticalTab = '\v'.charCodeAt(0); 245 var _backslash = '\\'.charCodeAt(0); 246 var _doubleQuote = '"'.charCodeAt(0); 247 248 var startPos = 0, chrCode, replaceWith = null, resultPieces = []; 249 250 for (var i = 0, len = text.length; i < len; i++) { 251 chrCode = text.charCodeAt(i); 252 switch (chrCode) { 253 case _backspace: 254 replaceWith = '\\b'; 255 break; 256 case _formFeed: 257 replaceWith = '\\f'; 258 break; 259 case _newLine: 260 replaceWith = '\\n'; 261 break; 262 case _nullChar: 263 replaceWith = '\\0'; 264 break; 265 case _carriageReturn: 266 replaceWith = '\\r'; 267 break; 268 case _tab: 269 replaceWith = '\\t'; 270 break; 271 case _verticalTab: 272 replaceWith = '\\v'; 273 break; 274 case _backslash: 275 replaceWith = '\\\\'; 276 break; 277 case _doubleQuote: 278 replaceWith = '\\"'; 279 break; 280 } 281 if (replaceWith !== null) { 282 resultPieces.push(text.substring(startPos, i)); 283 resultPieces.push(replaceWith); 284 startPos = i + 1; 285 replaceWith = null; 286 } 287 } 288 resultPieces.push(text.substring(startPos, len)); 289 return resultPieces.join(''); 290 } 291 292 /// End of import 293} 294 295const services = path.join(__dirname, '../built/local/typescriptServices.js'); 296fs.watchFile(services, () =>{ 297 console.log("Updating the monaco build") 298 updateTSDist() 299}) 300 301http.createServer(function (req, res) { 302 const incoming = url.parse(req.url) 303 if (incoming.path.endsWith("typescriptServices.js")) { 304 // Use the built version 305 res.writeHead(200, {"Content-Type": "text/javascript"}); 306 const amdPath = path.join(__dirname, '../internal/lib/typescriptServices-amd.js'); 307 res.write(fs.readFileSync(amdPath)) 308 } else { 309 // Redirect to the TS CDN 310 res.writeHead(302, { 311 'Location': `https://typescript.azureedge.net/cdn/3.9.2/monaco/${incoming.path}` 312 }); 313 } 314 315 res.end(); 316}).listen(5615); 317 318console.log("Starting servers\n") 319console.log(" - [✓] file watcher: " + services) 320console.log(" - [✓] http server: http://localhost:5615") 321 322console.log("\n\nGet started: http://www.staging-typescript.org/play?ts=dev") 323