servicesHost.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.getSolutionErrors = exports.makeSolutionBuilderHost = exports.makeWatchHost = exports.updateFileWithText = exports.makeServicesHost = void 0;
  4. const path = require("path");
  5. const config_1 = require("./config");
  6. const constants = require("./constants");
  7. const instances_1 = require("./instances");
  8. const resolver_1 = require("./resolver");
  9. const utils_1 = require("./utils");
  10. function makeResolversAndModuleResolutionHost(scriptRegex, loader, instance, fileExists, enableFileCaching) {
  11. const { compiler, compilerOptions, appendTsTsxSuffixesIfRequired, loaderOptions: { resolveModuleName: customResolveModuleName, resolveTypeReferenceDirective: customResolveTypeReferenceDirective, }, } = instance;
  12. const newLine = compilerOptions.newLine === constants.CarriageReturnLineFeedCode
  13. ? constants.CarriageReturnLineFeed
  14. : compilerOptions.newLine === constants.LineFeedCode
  15. ? constants.LineFeed
  16. : constants.EOL;
  17. // loader.context seems to work fine on Linux / Mac regardless causes problems for @types resolution on Windows for TypeScript < 2.3
  18. const getCurrentDirectory = () => loader.context;
  19. // make a (sync) resolver that follows webpack's rules
  20. const resolveSync = (0, resolver_1.makeResolver)(loader._compiler.options);
  21. const moduleResolutionHost = {
  22. trace: logData => instance.log.log(logData),
  23. fileExists,
  24. readFile,
  25. realpath: compiler.sys.realpath && realpath,
  26. directoryExists,
  27. getCurrentDirectory,
  28. getDirectories,
  29. readDirectory,
  30. useCaseSensitiveFileNames: () => (0, utils_1.useCaseSensitiveFileNames)(compiler, instance.loaderOptions),
  31. getNewLine: () => newLine,
  32. getDefaultLibFileName: options => compiler.getDefaultLibFilePath(options),
  33. };
  34. if (enableFileCaching) {
  35. addCache(moduleResolutionHost);
  36. }
  37. return makeResolvers(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, customResolveModuleName, resolveSync, appendTsTsxSuffixesIfRequired, scriptRegex, instance);
  38. function readFile(filePath, encoding) {
  39. return (instance.compiler.sys.readFile(filePath, encoding) ||
  40. (0, utils_1.fsReadFile)(filePath, encoding));
  41. }
  42. function directoryExists(directoryName) {
  43. return compiler.sys.directoryExists(directoryName);
  44. }
  45. function realpath(path) {
  46. return compiler.sys.realpath(path);
  47. }
  48. function getDirectories(path) {
  49. return compiler.sys.getDirectories(path);
  50. }
  51. function readDirectory(path, extensions, exclude, include, depth) {
  52. return compiler.sys.readDirectory(path, extensions, exclude, include, depth);
  53. }
  54. }
  55. /**
  56. * Create the TypeScript language service
  57. */
  58. function makeServicesHost(scriptRegex, loader, instance, projectReferences) {
  59. const { compiler, compilerOptions, files, filePathKeyMapper } = instance;
  60. const { moduleResolutionHost, resolveModuleNames, resolveTypeReferenceDirectives, } = makeResolversAndModuleResolutionHost(scriptRegex, loader, instance, filePathToCheck => compiler.sys.fileExists(filePathToCheck) ||
  61. (0, utils_1.fsReadFile)(filePathToCheck) !== undefined, instance.loaderOptions.experimentalFileCaching);
  62. const servicesHost = {
  63. getProjectVersion: () => `${instance.version}`,
  64. getProjectReferences: () => projectReferences,
  65. getScriptFileNames: () => [...files.values()]
  66. .map(({ fileName }) => fileName)
  67. .filter(filePath => filePath.match(scriptRegex)),
  68. getScriptVersion: (fileName) => {
  69. var _a;
  70. fileName = path.normalize(fileName);
  71. const key = filePathKeyMapper(fileName);
  72. const file = files.get(key);
  73. if (file) {
  74. return file.version.toString();
  75. }
  76. const outputFileAndKey = (_a = instance.solutionBuilderHost) === null || _a === void 0 ? void 0 : _a.getOutputFileAndKeyFromReferencedProject(fileName);
  77. if (outputFileAndKey !== undefined) {
  78. instance.solutionBuilderHost.outputAffectingInstanceVersion.set(outputFileAndKey.key, true);
  79. }
  80. return outputFileAndKey && outputFileAndKey.outputFile
  81. ? outputFileAndKey.outputFile
  82. : '';
  83. },
  84. getScriptSnapshot: (fileName) => {
  85. // This is called any time TypeScript needs a file's text
  86. // We either load from memory or from disk
  87. fileName = path.normalize(fileName);
  88. const key = filePathKeyMapper(fileName);
  89. let file = files.get(key);
  90. if (file === undefined) {
  91. if (instance.solutionBuilderHost) {
  92. const outputFileAndKey = instance.solutionBuilderHost.getOutputFileTextAndKeyFromReferencedProject(fileName);
  93. if (outputFileAndKey !== undefined) {
  94. instance.solutionBuilderHost.outputAffectingInstanceVersion.set(outputFileAndKey.key, true);
  95. return outputFileAndKey && outputFileAndKey.text !== undefined
  96. ? compiler.ScriptSnapshot.fromString(outputFileAndKey.text)
  97. : undefined;
  98. }
  99. }
  100. const text = moduleResolutionHost.readFile(fileName);
  101. if (text === undefined) {
  102. return undefined;
  103. }
  104. file = { fileName, version: 0, text };
  105. files.set(key, file);
  106. }
  107. return compiler.ScriptSnapshot.fromString(file.text);
  108. },
  109. ...moduleResolutionHost,
  110. getCompilationSettings: () => compilerOptions,
  111. log: moduleResolutionHost.trace,
  112. // used for (/// <reference types="...">) see https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/250#issuecomment-485061329
  113. resolveTypeReferenceDirectives,
  114. resolveModuleNames,
  115. getCustomTransformers: () => instance.transformers,
  116. };
  117. return servicesHost;
  118. }
  119. exports.makeServicesHost = makeServicesHost;
  120. function makeResolvers(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, customResolveModuleName, resolveSync, appendTsTsxSuffixesIfRequired, scriptRegex, instance) {
  121. const resolveModuleName = makeResolveModuleName(compiler, compilerOptions, moduleResolutionHost, customResolveModuleName, instance);
  122. const resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, _, containingSourceFile) => {
  123. const resolvedModules = moduleNames.map(moduleName => resolveModule(resolveSync, resolveModuleName, appendTsTsxSuffixesIfRequired, scriptRegex, moduleName, containingFile, redirectedReference, containingSourceFile));
  124. (0, utils_1.populateDependencyGraph)(resolvedModules, instance, containingFile);
  125. return resolvedModules;
  126. };
  127. const resolveTypeReferenceDirective = makeResolveTypeReferenceDirective(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, instance);
  128. const resolveTypeReferenceDirectives = (typeDirectiveNames, containingFile, redirectedReference, options, containingFileMode // new impliedNodeFormat is accepted by compilerHost
  129. ) => typeDirectiveNames.map(directive => resolveTypeReferenceDirective(directive, containingFile, options, redirectedReference, containingFileMode).resolvedTypeReferenceDirective);
  130. return {
  131. resolveTypeReferenceDirectives,
  132. resolveModuleNames,
  133. moduleResolutionHost,
  134. };
  135. }
  136. function createWatchFactory(filePathKeyMapper, compiler) {
  137. const watchedFiles = new Map();
  138. const watchedDirectories = new Map();
  139. const watchedDirectoriesRecursive = new Map();
  140. return {
  141. watchedFiles,
  142. watchedDirectories,
  143. watchedDirectoriesRecursive,
  144. invokeFileWatcher,
  145. watchFile,
  146. watchDirectory,
  147. };
  148. function invokeWatcherCallbacks(map, key, fileName, eventKind) {
  149. var _a;
  150. const callbacks = (_a = map.get(filePathKeyMapper(key))) === null || _a === void 0 ? void 0 : _a.callbacks;
  151. if (callbacks !== undefined && callbacks.length) {
  152. // The array copy is made to ensure that even if one of the callback removes the callbacks,
  153. // we dont miss any callbacks following it
  154. const cbs = callbacks.slice();
  155. for (const cb of cbs) {
  156. cb(fileName, eventKind);
  157. }
  158. return true;
  159. }
  160. return false;
  161. }
  162. function invokeFileWatcher(fileName, eventKind) {
  163. fileName = path.normalize(fileName);
  164. let result = invokeWatcherCallbacks(watchedFiles, fileName, fileName, eventKind);
  165. if (eventKind !== compiler.FileWatcherEventKind.Changed) {
  166. const directory = path.dirname(fileName);
  167. result =
  168. invokeWatcherCallbacks(watchedDirectories, directory, fileName) ||
  169. result;
  170. result = invokeRecursiveDirectoryWatcher(directory, fileName) || result;
  171. }
  172. return result;
  173. }
  174. ``;
  175. function invokeRecursiveDirectoryWatcher(directory, fileAddedOrRemoved) {
  176. directory = path.normalize(directory);
  177. let result = invokeWatcherCallbacks(watchedDirectoriesRecursive, directory, fileAddedOrRemoved);
  178. const basePath = path.dirname(directory);
  179. if (directory !== basePath) {
  180. result =
  181. invokeRecursiveDirectoryWatcher(basePath, fileAddedOrRemoved) || result;
  182. }
  183. return result;
  184. }
  185. function createWatcher(file, callbacks, callback) {
  186. const key = filePathKeyMapper(file);
  187. const existing = callbacks.get(key);
  188. if (existing === undefined) {
  189. callbacks.set(key, {
  190. fileName: path.normalize(file),
  191. callbacks: [callback],
  192. });
  193. }
  194. else {
  195. existing.callbacks.push(callback);
  196. }
  197. return {
  198. close: () => {
  199. const existing = callbacks.get(key);
  200. if (existing !== undefined) {
  201. (0, utils_1.unorderedRemoveItem)(existing.callbacks, callback);
  202. if (!existing.callbacks.length) {
  203. callbacks.delete(key);
  204. }
  205. }
  206. },
  207. };
  208. }
  209. function watchFile(fileName, callback, _pollingInterval) {
  210. return createWatcher(fileName, watchedFiles, callback);
  211. }
  212. function watchDirectory(fileName, callback, recursive) {
  213. return createWatcher(fileName, recursive === true ? watchedDirectoriesRecursive : watchedDirectories, callback);
  214. }
  215. }
  216. function updateFileWithText(instance, key, filePath, text) {
  217. const nFilePath = path.normalize(filePath);
  218. const file = instance.files.get(key) || instance.otherFiles.get(key);
  219. if (file !== undefined) {
  220. const newText = text(nFilePath);
  221. if (newText !== file.text) {
  222. file.text = newText;
  223. file.version++;
  224. file.modifiedTime = new Date();
  225. instance.version++;
  226. if (!instance.modifiedFiles) {
  227. instance.modifiedFiles = new Map();
  228. }
  229. instance.modifiedFiles.set(key, true);
  230. if (instance.watchHost !== undefined) {
  231. instance.watchHost.invokeFileWatcher(nFilePath, instance.compiler.FileWatcherEventKind.Changed);
  232. }
  233. }
  234. }
  235. }
  236. exports.updateFileWithText = updateFileWithText;
  237. /**
  238. * Create the TypeScript Watch host
  239. */
  240. function makeWatchHost(scriptRegex, loader, instance, projectReferences) {
  241. const { compiler, compilerOptions, files, otherFiles, filePathKeyMapper } = instance;
  242. const { watchFile, watchDirectory, invokeFileWatcher } = createWatchFactory(filePathKeyMapper, compiler);
  243. const { moduleResolutionHost, resolveModuleNames, resolveTypeReferenceDirectives, } = makeResolversAndModuleResolutionHost(scriptRegex, loader, instance, fileName => files.has(filePathKeyMapper(fileName)) ||
  244. compiler.sys.fileExists(fileName), instance.loaderOptions.experimentalFileCaching);
  245. const watchHost = {
  246. rootFiles: getRootFileNames(),
  247. options: compilerOptions,
  248. ...moduleResolutionHost,
  249. readFile: readFileWithCachingText,
  250. watchFile: (fileName, callback, pollingInterval, options) => {
  251. var _a;
  252. const outputFileKey = (_a = instance.solutionBuilderHost) === null || _a === void 0 ? void 0 : _a.getOutputFileKeyFromReferencedProject(fileName);
  253. if (!outputFileKey || outputFileKey === filePathKeyMapper(fileName)) {
  254. return watchFile(fileName, callback, pollingInterval, options);
  255. }
  256. // Handle symlink to outputFile
  257. const outputFileName = instance.solutionBuilderHost.realpath(fileName);
  258. return watchFile(outputFileName, (_fileName, eventKind) => callback(fileName, eventKind), pollingInterval, options);
  259. },
  260. watchDirectory,
  261. // used for (/// <reference types="...">) see https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/250#issuecomment-485061329
  262. resolveTypeReferenceDirectives,
  263. resolveModuleNames,
  264. invokeFileWatcher,
  265. updateRootFileNames: () => {
  266. instance.changedFilesList = false;
  267. if (instance.watchOfFilesAndCompilerOptions !== undefined) {
  268. instance.watchOfFilesAndCompilerOptions.updateRootFileNames(getRootFileNames());
  269. }
  270. },
  271. createProgram: projectReferences === undefined
  272. ? compiler.createEmitAndSemanticDiagnosticsBuilderProgram
  273. : createBuilderProgramWithReferences,
  274. outputFiles: new Map(),
  275. };
  276. return watchHost;
  277. function getRootFileNames() {
  278. return [...files.values()]
  279. .map(({ fileName }) => fileName)
  280. .filter(filePath => filePath.match(scriptRegex));
  281. }
  282. function readFileWithCachingText(fileName, encoding) {
  283. var _a;
  284. fileName = path.normalize(fileName);
  285. const key = filePathKeyMapper(fileName);
  286. const file = files.get(key) || otherFiles.get(key);
  287. if (file !== undefined) {
  288. return file.text;
  289. }
  290. const text = moduleResolutionHost.readFile(fileName, encoding);
  291. if (text === undefined) {
  292. return undefined;
  293. }
  294. if (!((_a = instance.solutionBuilderHost) === null || _a === void 0 ? void 0 : _a.getOutputFileKeyFromReferencedProject(fileName))) {
  295. otherFiles.set(key, { fileName, version: 0, text });
  296. }
  297. return text;
  298. }
  299. function createBuilderProgramWithReferences(rootNames, options, host, oldProgram, configFileParsingDiagnostics) {
  300. const program = compiler.createProgram({
  301. rootNames: rootNames,
  302. options: options,
  303. host,
  304. oldProgram: oldProgram && oldProgram.getProgram(),
  305. configFileParsingDiagnostics,
  306. projectReferences,
  307. });
  308. const builderProgramHost = host;
  309. return compiler.createEmitAndSemanticDiagnosticsBuilderProgram(program, builderProgramHost, oldProgram, configFileParsingDiagnostics);
  310. }
  311. }
  312. exports.makeWatchHost = makeWatchHost;
  313. const missingFileModifiedTime = new Date(0);
  314. function identity(x) {
  315. return x;
  316. }
  317. function toLowerCase(x) {
  318. return x.toLowerCase();
  319. }
  320. const fileNameLowerCaseRegExp = /[^\u0130\u0131\u00DFa-z0-9\\/:\-_\. ]+/g;
  321. function toFileNameLowerCase(x) {
  322. return fileNameLowerCaseRegExp.test(x)
  323. ? x.replace(fileNameLowerCaseRegExp, toLowerCase)
  324. : x;
  325. }
  326. function createGetCanonicalFileName(instance) {
  327. return (0, utils_1.useCaseSensitiveFileNames)(instance.compiler, instance.loaderOptions)
  328. ? identity
  329. : toFileNameLowerCase;
  330. }
  331. function createModuleResolutionCache(instance, moduleResolutionHost) {
  332. const cache = instance.compiler.createModuleResolutionCache(moduleResolutionHost.getCurrentDirectory(), createGetCanonicalFileName(instance), instance.compilerOptions);
  333. // Add new API optional methods
  334. if (!cache.clear) {
  335. cache.clear = () => {
  336. cache.directoryToModuleNameMap.clear();
  337. cache.moduleNameToDirectoryMap.clear();
  338. };
  339. }
  340. if (!cache.update) {
  341. cache.update = options => {
  342. if (!options.configFile)
  343. return;
  344. const ref = {
  345. sourceFile: options.configFile,
  346. commandLine: { options },
  347. };
  348. cache.directoryToModuleNameMap.setOwnMap(cache.directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(ref));
  349. cache.moduleNameToDirectoryMap.setOwnMap(cache.moduleNameToDirectoryMap.getOrCreateMapOfCacheRedirects(ref));
  350. cache.directoryToModuleNameMap.setOwnOptions(options);
  351. cache.moduleNameToDirectoryMap.setOwnOptions(options);
  352. };
  353. }
  354. return cache;
  355. }
  356. /**
  357. * Create the TypeScript Watch host
  358. */
  359. function makeSolutionBuilderHost(scriptRegex, loader, instance) {
  360. const { compiler, loaderOptions: { transpileOnly }, filePathKeyMapper, } = instance;
  361. // loader.context seems to work fine on Linux / Mac regardless causes problems for @types resolution on Windows for TypeScript < 2.3
  362. const formatDiagnosticHost = {
  363. getCurrentDirectory: compiler.sys.getCurrentDirectory,
  364. getCanonicalFileName: createGetCanonicalFileName(instance),
  365. getNewLine: () => compiler.sys.newLine,
  366. };
  367. const diagnostics = {
  368. global: [],
  369. perFile: new Map(),
  370. transpileErrors: [],
  371. };
  372. const reportDiagnostic = (d) => {
  373. if (transpileOnly) {
  374. const filePath = d.file ? filePathKeyMapper(d.file.fileName) : undefined;
  375. const last = diagnostics.transpileErrors[diagnostics.transpileErrors.length - 1];
  376. if (diagnostics.transpileErrors.length && last[0] === filePath) {
  377. last[1].push(d);
  378. }
  379. else {
  380. diagnostics.transpileErrors.push([filePath, [d]]);
  381. }
  382. }
  383. else if (d.file) {
  384. const filePath = filePathKeyMapper(d.file.fileName);
  385. const existing = diagnostics.perFile.get(filePath);
  386. if (existing) {
  387. existing.push(d);
  388. }
  389. else {
  390. diagnostics.perFile.set(filePath, [d]);
  391. }
  392. }
  393. else {
  394. diagnostics.global.push(d);
  395. }
  396. instance.log.logInfo(compiler.formatDiagnostic(d, formatDiagnosticHost));
  397. };
  398. const reportSolutionBuilderStatus = (d) => instance.log.logInfo(compiler.formatDiagnostic(d, formatDiagnosticHost));
  399. const reportWatchStatus = (d, newLine, _options) => instance.log.logInfo(`${compiler.flattenDiagnosticMessageText(d.messageText, compiler.sys.newLine)}${newLine + newLine}`);
  400. const outputFiles = new Map();
  401. const inputFiles = new Map();
  402. const writtenFiles = [];
  403. const outputAffectingInstanceVersion = new Map();
  404. let timeoutId;
  405. const { resolveModuleNames, resolveTypeReferenceDirectives, moduleResolutionHost, } = makeResolversAndModuleResolutionHost(scriptRegex, loader, instance, fileName => {
  406. const filePathKey = filePathKeyMapper(fileName);
  407. return (instance.files.has(filePathKey) ||
  408. instance.otherFiles.has(filePathKey) ||
  409. compiler.sys.fileExists(fileName));
  410. },
  411. /*enableFileCaching*/ true);
  412. const configFileInfo = new Map();
  413. const allWatches = [];
  414. const sysHost = compiler.createSolutionBuilderWithWatchHost(compiler.sys, compiler.createEmitAndSemanticDiagnosticsBuilderProgram, reportDiagnostic, reportSolutionBuilderStatus, reportWatchStatus);
  415. // Keeps track of the various `typescript.CustomTransformers` for each program that is created.
  416. const customTransformers = new Map();
  417. // let lastBuilderProgram: typescript.CreateProgram | undefined = undefined;
  418. const solutionBuilderHost = {
  419. ...sysHost,
  420. ...moduleResolutionHost,
  421. createProgram: (rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) => {
  422. var _a, _b, _c, _d;
  423. (_a = instance.moduleResolutionCache) === null || _a === void 0 ? void 0 : _a.update(options || {});
  424. (_b = instance.typeReferenceResolutionCache) === null || _b === void 0 ? void 0 : _b.update(options || {});
  425. const result = sysHost.createProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
  426. (_c = instance.typeReferenceResolutionCache) === null || _c === void 0 ? void 0 : _c.update(instance.compilerOptions);
  427. (_d = instance.moduleResolutionCache) === null || _d === void 0 ? void 0 : _d.update(instance.compilerOptions);
  428. if (options) {
  429. // The `configFilePath` is the same value that is used as the `project` parameter of
  430. // `getCustomtransformers` below.
  431. const project = options.configFilePath;
  432. if (typeof project === "string") {
  433. // Custom transformers need a reference to the `typescript.Program`, that reference is
  434. // unavailable during the the `getCustomTransformers` callback below.
  435. const transformers = (0, instances_1.getCustomTransformers)(instance.loaderOptions, result.getProgram(), result.getProgram);
  436. customTransformers.set(project, transformers);
  437. }
  438. }
  439. return result;
  440. },
  441. resolveModuleNames,
  442. resolveTypeReferenceDirectives,
  443. diagnostics,
  444. ...createWatchFactory(filePathKeyMapper, compiler),
  445. getCustomTransformers: function (project) {
  446. return customTransformers.get(project);
  447. },
  448. // Overrides
  449. writeFile: (name, text, writeByteOrderMark) => {
  450. var _a;
  451. const key = filePathKeyMapper(name);
  452. updateFileWithText(instance, key, name, () => text);
  453. const existing = ensureOutputFile(name);
  454. const hash = hashOutputText(text);
  455. outputFiles.set(key, hash);
  456. writtenFiles.push({
  457. name,
  458. text,
  459. writeByteOrderMark: !!writeByteOrderMark,
  460. });
  461. compiler.sys.writeFile(name, text, writeByteOrderMark);
  462. (_a = moduleResolutionHost.fileExistsCache) === null || _a === void 0 ? void 0 : _a.delete(name);
  463. if (outputAffectingInstanceVersion.has(key) &&
  464. (!existing || existing !== hash)) {
  465. instance.version++;
  466. }
  467. if (instance.watchHost &&
  468. !instance.files.has(key) &&
  469. !instance.otherFiles.has(key)) {
  470. // If file wasnt updated in files or other files of instance, let watch host know of the change
  471. if (!existing) {
  472. instance.hasUnaccountedModifiedFiles =
  473. instance.watchHost.invokeFileWatcher(name, compiler.FileWatcherEventKind.Created) || instance.hasUnaccountedModifiedFiles;
  474. }
  475. else if (existing !== hash) {
  476. instance.hasUnaccountedModifiedFiles =
  477. instance.watchHost.invokeFileWatcher(name, compiler.FileWatcherEventKind.Changed) || instance.hasUnaccountedModifiedFiles;
  478. }
  479. }
  480. },
  481. createDirectory: sysHost.createDirectory &&
  482. (directory => {
  483. var _a;
  484. sysHost.createDirectory(directory);
  485. (_a = moduleResolutionHost.directoryExistsCache) === null || _a === void 0 ? void 0 : _a.delete(directory);
  486. }),
  487. afterProgramEmitAndDiagnostics: transpileOnly ? undefined : storeDtsFiles,
  488. setTimeout: (callback, _time, ...args) => {
  489. timeoutId = [callback, args];
  490. return timeoutId;
  491. },
  492. clearTimeout: _timeoutId => {
  493. timeoutId = undefined;
  494. },
  495. getParsedCommandLine: file => {
  496. const config = (0, config_1.getParsedCommandLine)(compiler, instance.loaderOptions, file);
  497. configFileInfo.set(filePathKeyMapper(file), { config });
  498. return config;
  499. },
  500. writtenFiles,
  501. configFileInfo,
  502. outputAffectingInstanceVersion,
  503. getInputFileStamp,
  504. updateSolutionBuilderInputFile,
  505. getOutputFileKeyFromReferencedProject,
  506. getOutputFileAndKeyFromReferencedProject,
  507. getOutputFileTextAndKeyFromReferencedProject,
  508. getInputFileNameFromOutput: fileName => {
  509. const result = getInputFileNameFromOutput(fileName);
  510. return typeof result === 'string' ? result : undefined;
  511. },
  512. getOutputFilesFromReferencedProjectInput,
  513. buildReferences,
  514. ensureAllReferenceTimestamps,
  515. clearCache,
  516. close,
  517. };
  518. return solutionBuilderHost;
  519. function close() {
  520. allWatches.slice().forEach(w => w.close());
  521. }
  522. function clearCache() {
  523. moduleResolutionHost.clearCache();
  524. outputFiles.clear();
  525. inputFiles.clear();
  526. }
  527. function buildReferences() {
  528. if (!timeoutId) {
  529. ensureAllReferenceTimestamps();
  530. return;
  531. }
  532. diagnostics.global.length = 0;
  533. diagnostics.perFile.clear();
  534. diagnostics.transpileErrors.length = 0;
  535. while (timeoutId) {
  536. const [callback, args] = timeoutId;
  537. timeoutId = undefined;
  538. callback(...args);
  539. }
  540. ensureAllReferenceTimestamps();
  541. }
  542. function ensureAllReferenceTimestamps() {
  543. if (inputFiles.size !== solutionBuilderHost.watchedFiles.size) {
  544. for (const { fileName, } of instance.solutionBuilderHost.watchedFiles.values()) {
  545. instance.solutionBuilderHost.getInputFileStamp(fileName);
  546. }
  547. }
  548. }
  549. function storeDtsFiles(builderProgram) {
  550. const program = builderProgram.getProgram();
  551. for (const configInfo of configFileInfo.values()) {
  552. if (!configInfo.config ||
  553. program.getRootFileNames() !== configInfo.config.fileNames ||
  554. program.getCompilerOptions() !== configInfo.config.options ||
  555. program.getProjectReferences() !== configInfo.config.projectReferences) {
  556. continue;
  557. }
  558. configInfo.dtsFiles = program
  559. .getSourceFiles()
  560. .map(file => path.resolve(file.fileName))
  561. .filter(fileName => fileName.match(constants.dtsDtsxOrDtsDtsxMapRegex));
  562. return;
  563. }
  564. }
  565. function getInputFileNameFromOutput(outputFileName) {
  566. const resolvedFileName = filePathKeyMapper(outputFileName);
  567. for (const configInfo of configFileInfo.values()) {
  568. ensureInputOutputInfo(configInfo);
  569. if (configInfo.outputFileNames) {
  570. for (const { inputFileName, outputNames, } of configInfo.outputFileNames.values()) {
  571. if (outputNames.some(outputName => resolvedFileName === filePathKeyMapper(outputName))) {
  572. return inputFileName;
  573. }
  574. }
  575. }
  576. if (configInfo.tsbuildInfoFile &&
  577. filePathKeyMapper(configInfo.tsbuildInfoFile) === resolvedFileName) {
  578. return true;
  579. }
  580. }
  581. const realPath = solutionBuilderHost.realpath(outputFileName);
  582. return filePathKeyMapper(realPath) !== resolvedFileName
  583. ? getInputFileNameFromOutput(realPath)
  584. : undefined;
  585. }
  586. function ensureInputOutputInfo(configInfo) {
  587. if (configInfo.outputFileNames || !configInfo.config) {
  588. return;
  589. }
  590. configInfo.outputFileNames = new Map();
  591. configInfo.config.fileNames.forEach(inputFile => configInfo.outputFileNames.set(filePathKeyMapper(inputFile), {
  592. inputFileName: path.resolve(inputFile),
  593. outputNames: (0, instances_1.getOutputFileNames)(instance, configInfo.config, inputFile),
  594. }));
  595. configInfo.tsbuildInfoFile = instance.compiler
  596. .getTsBuildInfoEmitOutputFilePath
  597. ? instance.compiler.getTsBuildInfoEmitOutputFilePath(configInfo.config.options)
  598. : // before api
  599. instance.compiler.getOutputPathForBuildInfo(configInfo.config.options);
  600. }
  601. function getOutputFileAndKeyFromReferencedProject(outputFileName) {
  602. const outputFile = ensureOutputFile(outputFileName);
  603. return outputFile !== undefined
  604. ? {
  605. key: getOutputFileKeyFromReferencedProject(outputFileName),
  606. outputFile,
  607. }
  608. : undefined;
  609. }
  610. function getOutputFileTextAndKeyFromReferencedProject(outputFileName) {
  611. const key = getOutputFileKeyFromReferencedProject(outputFileName);
  612. if (!key) {
  613. return undefined;
  614. }
  615. const file = writtenFiles.find(w => filePathKeyMapper(w.name) === key);
  616. if (file) {
  617. return { key, text: file.text };
  618. }
  619. const outputFile = outputFiles.get(key);
  620. return {
  621. key,
  622. text: outputFile !== false
  623. ? compiler.sys.readFile(outputFileName)
  624. : undefined,
  625. };
  626. }
  627. function getOutputFileKeyFromReferencedProject(outputFileName) {
  628. const key = filePathKeyMapper(outputFileName);
  629. if (outputFiles.has(key))
  630. return key;
  631. const realKey = filePathKeyMapper(solutionBuilderHost.realpath(outputFileName));
  632. if (realKey !== key && outputFiles.has(realKey))
  633. return realKey;
  634. return getInputFileNameFromOutput(outputFileName) ? realKey : undefined;
  635. }
  636. function hashOutputText(text) {
  637. return compiler.sys.createHash ? compiler.sys.createHash(text) : text;
  638. }
  639. function ensureOutputFile(outputFileName) {
  640. const key = getOutputFileKeyFromReferencedProject(outputFileName);
  641. if (!key) {
  642. return undefined;
  643. }
  644. const outputFile = outputFiles.get(key);
  645. if (outputFile !== undefined) {
  646. return outputFile;
  647. }
  648. if (!getInputFileNameFromOutput(outputFileName)) {
  649. return undefined;
  650. }
  651. const text = compiler.sys.readFile(outputFileName);
  652. const hash = text === undefined ? false : hashOutputText(text);
  653. outputFiles.set(key, hash);
  654. return hash;
  655. }
  656. function getTypeScriptOutputFile(outputFileName) {
  657. const key = filePathKeyMapper(outputFileName);
  658. const writtenFile = writtenFiles.find(w => filePathKeyMapper(w.name) === key);
  659. if (writtenFile)
  660. return writtenFile;
  661. // Read from sys
  662. const text = compiler.sys.readFile(outputFileName);
  663. return text !== undefined
  664. ? {
  665. name: outputFileName,
  666. text,
  667. writeByteOrderMark: false,
  668. }
  669. : undefined;
  670. }
  671. function getOutputFilesFromReferencedProjectInput(inputFileName) {
  672. const resolvedFileName = filePathKeyMapper(inputFileName);
  673. for (const configInfo of configFileInfo.values()) {
  674. ensureInputOutputInfo(configInfo);
  675. if (configInfo.outputFileNames) {
  676. const result = configInfo.outputFileNames.get(resolvedFileName);
  677. if (result) {
  678. return result.outputNames
  679. .map(getTypeScriptOutputFile)
  680. .filter(output => !!output);
  681. }
  682. }
  683. }
  684. return [];
  685. }
  686. function getInputFileStamp(fileName) {
  687. const key = filePathKeyMapper(fileName);
  688. const existing = inputFiles.get(key);
  689. if (existing !== undefined) {
  690. return existing;
  691. }
  692. const time = compiler.sys.getModifiedTime(fileName) || missingFileModifiedTime;
  693. inputFiles.set(key, time);
  694. return time;
  695. }
  696. function updateSolutionBuilderInputFile(fileName) {
  697. const key = filePathKeyMapper(fileName);
  698. const existing = inputFiles.get(key) || missingFileModifiedTime;
  699. const newTime = compiler.sys.getModifiedTime(fileName) || missingFileModifiedTime;
  700. if (existing.getTime() === newTime.getTime()) {
  701. return;
  702. }
  703. const eventKind = existing == missingFileModifiedTime
  704. ? compiler.FileWatcherEventKind.Created
  705. : newTime === missingFileModifiedTime
  706. ? compiler.FileWatcherEventKind.Deleted
  707. : compiler.FileWatcherEventKind.Changed;
  708. solutionBuilderHost.invokeFileWatcher(fileName, eventKind);
  709. }
  710. }
  711. exports.makeSolutionBuilderHost = makeSolutionBuilderHost;
  712. function getSolutionErrors(instance, context) {
  713. const solutionErrors = [];
  714. if (instance.solutionBuilderHost &&
  715. instance.solutionBuilderHost.diagnostics.transpileErrors.length) {
  716. instance.solutionBuilderHost.diagnostics.transpileErrors.forEach(([filePath, errors]) => solutionErrors.push(...(0, utils_1.formatErrors)(errors, instance.loaderOptions, instance.colors, instance.compiler, { file: filePath ? undefined : 'tsconfig.json' }, context)));
  717. }
  718. return solutionErrors;
  719. }
  720. exports.getSolutionErrors = getSolutionErrors;
  721. function makeResolveTypeReferenceDirective(compiler, compilerOptions, moduleResolutionHost, customResolveTypeReferenceDirective, instance) {
  722. var _a, _b;
  723. if (customResolveTypeReferenceDirective === undefined) {
  724. // Until the api is published
  725. if (compiler.createTypeReferenceDirectiveResolutionCache !== undefined &&
  726. !instance.typeReferenceResolutionCache) {
  727. instance.typeReferenceResolutionCache =
  728. compiler.createTypeReferenceDirectiveResolutionCache(moduleResolutionHost.getCurrentDirectory(), createGetCanonicalFileName(instance), instance.compilerOptions, (_b = (_a = instance.moduleResolutionCache) === null || _a === void 0 ? void 0 : _a.getPackageJsonInfoCache) === null || _b === void 0 ? void 0 : _b.call(_a));
  729. }
  730. return (typeDirectiveName, containingFile, options, redirectedReference, containingFileMode) => {
  731. // Copy-pasted from https://github.com/TypeStrong/ts-node/blob/9f789d0d91c6eba30ac7f7aad45194a23b44f159/src/resolver-functions.ts#L139
  732. const nameIsString = typeof typeDirectiveName === 'string';
  733. const mode = nameIsString
  734. ? undefined
  735. : compiler.getModeForFileReference(typeDirectiveName, containingFileMode);
  736. const strName = nameIsString
  737. ? typeDirectiveName
  738. : typeDirectiveName.fileName.toLowerCase();
  739. return compiler.resolveTypeReferenceDirective(strName, containingFile, options, moduleResolutionHost, redirectedReference, undefined, mode);
  740. };
  741. }
  742. return (directive, containingFile) => customResolveTypeReferenceDirective(directive, // unsure whether we should evolve this further
  743. containingFile, compilerOptions, moduleResolutionHost, compiler.resolveTypeReferenceDirective);
  744. }
  745. function isJsImplementationOfTypings(resolvedModule, tsResolution) {
  746. return (resolvedModule.resolvedFileName.endsWith('js') &&
  747. /\.d\.ts$/.test(tsResolution.resolvedFileName));
  748. }
  749. function resolveModule(resolveSync, resolveModuleName, appendTsTsxSuffixesIfRequired, scriptRegex, moduleName, containingFile, redirectedReference, containingSourceFile) {
  750. let resolutionResult;
  751. try {
  752. const originalFileName = resolveSync(path.normalize(path.dirname(containingFile)), moduleName);
  753. if (originalFileName) {
  754. const resolvedFileName = appendTsTsxSuffixesIfRequired(originalFileName);
  755. if (resolvedFileName.match(scriptRegex) !== null) {
  756. resolutionResult = { resolvedFileName, originalFileName };
  757. }
  758. }
  759. }
  760. catch (e) { }
  761. const tsResolution = resolveModuleName(moduleName, containingFile, redirectedReference, containingSourceFile);
  762. if (tsResolution.resolvedModule !== undefined) {
  763. const resolvedFileName = path.normalize(tsResolution.resolvedModule.resolvedFileName);
  764. const tsResolutionResult = {
  765. ...tsResolution.resolvedModule,
  766. originalFileName: resolvedFileName,
  767. resolvedFileName,
  768. };
  769. return resolutionResult === undefined ||
  770. resolutionResult.resolvedFileName ===
  771. tsResolutionResult.resolvedFileName ||
  772. isJsImplementationOfTypings(resolutionResult, tsResolutionResult)
  773. ? tsResolutionResult
  774. : resolutionResult;
  775. }
  776. return resolutionResult;
  777. }
  778. function makeResolveModuleName(compiler, compilerOptions, moduleResolutionHost, customResolveModuleName, instance) {
  779. if (customResolveModuleName === undefined) {
  780. if (!instance.moduleResolutionCache) {
  781. instance.moduleResolutionCache = createModuleResolutionCache(instance, moduleResolutionHost);
  782. }
  783. return (moduleName, containingFileName, redirectedReference, containingFile) => compiler.resolveModuleName(moduleName, containingFileName, compilerOptions, moduleResolutionHost, instance.moduleResolutionCache, redirectedReference, containingFile === null || containingFile === void 0 ? void 0 : containingFile.impliedNodeFormat);
  784. }
  785. return (moduleName, containingFile) => customResolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost, compiler.resolveModuleName);
  786. }
  787. function addCache(host) {
  788. host.fileExists = createCache(host.fileExists, (host.fileExistsCache = new Map()));
  789. host.directoryExists = createCache(host.directoryExists, (host.directoryExistsCache = new Map()));
  790. host.realpath =
  791. host.realpath &&
  792. createCache(host.realpath, (host.realpathCache = new Map()));
  793. host.clearCache = clearCache;
  794. function createCache(originalFunction, cache) {
  795. return function getCached(arg) {
  796. let res = cache.get(arg);
  797. if (res !== undefined) {
  798. return res;
  799. }
  800. res = originalFunction(arg);
  801. cache.set(arg, res);
  802. return res;
  803. };
  804. }
  805. function clearCache() {
  806. var _a, _b, _c;
  807. (_a = host.fileExistsCache) === null || _a === void 0 ? void 0 : _a.clear();
  808. (_b = host.directoryExistsCache) === null || _b === void 0 ? void 0 : _b.clear();
  809. (_c = host.realpathCache) === null || _c === void 0 ? void 0 : _c.clear();
  810. }
  811. }
  812. //# sourceMappingURL=servicesHost.js.map