選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

utils.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const fs = require("fs");
  4. const micromatch = require("micromatch");
  5. const path = require("path");
  6. const typescript = require("typescript");
  7. const constants = require("./constants");
  8. /**
  9. * The default error formatter.
  10. */
  11. function defaultErrorFormatter(error, colors) {
  12. const messageColor = error.severity === 'warning' ? colors.bold.yellow : colors.bold.red;
  13. return (colors.grey('[tsl] ') +
  14. messageColor(error.severity.toUpperCase()) +
  15. (error.file === ''
  16. ? ''
  17. : messageColor(' in ') +
  18. colors.bold.cyan(`${error.file}(${error.line},${error.character})`)) +
  19. constants.EOL +
  20. messageColor(` TS${error.code}: ${error.content}`));
  21. }
  22. /**
  23. * Take TypeScript errors, parse them and format to webpack errors
  24. * Optionally adds a file name
  25. */
  26. function formatErrors(diagnostics, loaderOptions, colors, compiler, merge, context) {
  27. return diagnostics === undefined
  28. ? []
  29. : diagnostics
  30. .filter(diagnostic => {
  31. if (loaderOptions.ignoreDiagnostics.indexOf(diagnostic.code) !== -1) {
  32. return false;
  33. }
  34. if (loaderOptions.reportFiles.length > 0 &&
  35. diagnostic.file !== undefined) {
  36. const relativeFileName = path.relative(context, diagnostic.file.fileName);
  37. const matchResult = micromatch([relativeFileName], loaderOptions.reportFiles);
  38. if (matchResult.length === 0) {
  39. return false;
  40. }
  41. }
  42. return true;
  43. })
  44. .map(diagnostic => {
  45. const file = diagnostic.file;
  46. const position = file === undefined
  47. ? undefined
  48. : file.getLineAndCharacterOfPosition(diagnostic.start);
  49. const errorInfo = {
  50. code: diagnostic.code,
  51. severity: compiler.DiagnosticCategory[diagnostic.category].toLowerCase(),
  52. content: compiler.flattenDiagnosticMessageText(diagnostic.messageText, constants.EOL),
  53. file: file === undefined ? '' : path.normalize(file.fileName),
  54. line: position === undefined ? 0 : position.line + 1,
  55. character: position === undefined ? 0 : position.character + 1,
  56. context
  57. };
  58. const message = loaderOptions.errorFormatter === undefined
  59. ? defaultErrorFormatter(errorInfo, colors)
  60. : loaderOptions.errorFormatter(errorInfo, colors);
  61. const error = makeError(message, merge.file === undefined ? errorInfo.file : merge.file, position === undefined
  62. ? undefined
  63. : { line: errorInfo.line, character: errorInfo.character });
  64. return Object.assign(error, merge);
  65. });
  66. }
  67. exports.formatErrors = formatErrors;
  68. function readFile(fileName, encoding = 'utf8') {
  69. fileName = path.normalize(fileName);
  70. try {
  71. return fs.readFileSync(fileName, encoding);
  72. }
  73. catch (e) {
  74. return undefined;
  75. }
  76. }
  77. exports.readFile = readFile;
  78. function makeError(message, file, location) {
  79. return {
  80. message,
  81. location,
  82. file,
  83. loaderSource: 'ts-loader'
  84. };
  85. }
  86. exports.makeError = makeError;
  87. function appendSuffixIfMatch(patterns, filePath, suffix) {
  88. if (patterns.length > 0) {
  89. for (const regexp of patterns) {
  90. if (filePath.match(regexp) !== null) {
  91. return filePath + suffix;
  92. }
  93. }
  94. }
  95. return filePath;
  96. }
  97. exports.appendSuffixIfMatch = appendSuffixIfMatch;
  98. function appendSuffixesIfMatch(suffixDict, filePath) {
  99. let amendedPath = filePath;
  100. for (const suffix in suffixDict) {
  101. amendedPath = appendSuffixIfMatch(suffixDict[suffix], amendedPath, suffix);
  102. }
  103. return amendedPath;
  104. }
  105. exports.appendSuffixesIfMatch = appendSuffixesIfMatch;
  106. function unorderedRemoveItem(array, item) {
  107. for (let i = 0; i < array.length; i++) {
  108. if (array[i] === item) {
  109. // Fill in the "hole" left at `index`.
  110. array[i] = array[array.length - 1];
  111. array.pop();
  112. return true;
  113. }
  114. }
  115. return false;
  116. }
  117. exports.unorderedRemoveItem = unorderedRemoveItem;
  118. /**
  119. * Recursively collect all possible dependants of passed file
  120. */
  121. function collectAllDependants(reverseDependencyGraph, fileName, collected = {}) {
  122. const result = {};
  123. result[fileName] = true;
  124. collected[fileName] = true;
  125. const dependants = reverseDependencyGraph[fileName];
  126. if (dependants !== undefined) {
  127. Object.keys(dependants).forEach(dependantFileName => {
  128. if (!collected[dependantFileName]) {
  129. collectAllDependants(reverseDependencyGraph, dependantFileName, collected).forEach(fName => (result[fName] = true));
  130. }
  131. });
  132. }
  133. return Object.keys(result);
  134. }
  135. exports.collectAllDependants = collectAllDependants;
  136. /**
  137. * Recursively collect all possible dependencies of passed file
  138. */
  139. function collectAllDependencies(dependencyGraph, filePath, collected = {}) {
  140. const result = {};
  141. result[filePath] = true;
  142. collected[filePath] = true;
  143. const directDependencies = dependencyGraph[filePath];
  144. if (directDependencies !== undefined) {
  145. directDependencies.forEach(dependencyModule => {
  146. if (!collected[dependencyModule.originalFileName]) {
  147. collectAllDependencies(dependencyGraph, dependencyModule.resolvedFileName, collected).forEach(depFilePath => (result[depFilePath] = true));
  148. }
  149. });
  150. }
  151. return Object.keys(result);
  152. }
  153. exports.collectAllDependencies = collectAllDependencies;
  154. function arrify(val) {
  155. if (val === null || val === undefined) {
  156. return [];
  157. }
  158. return Array.isArray(val) ? val : [val];
  159. }
  160. exports.arrify = arrify;
  161. function ensureProgram(instance) {
  162. if (instance && instance.watchHost) {
  163. if (instance.hasUnaccountedModifiedFiles) {
  164. if (instance.changedFilesList) {
  165. instance.watchHost.updateRootFileNames();
  166. }
  167. if (instance.watchOfFilesAndCompilerOptions) {
  168. instance.program = instance.watchOfFilesAndCompilerOptions
  169. .getProgram()
  170. .getProgram();
  171. }
  172. instance.hasUnaccountedModifiedFiles = false;
  173. }
  174. return instance.program;
  175. }
  176. if (instance.languageService) {
  177. return instance.languageService.getProgram();
  178. }
  179. return instance.program;
  180. }
  181. exports.ensureProgram = ensureProgram;
  182. function supportsProjectReferences(instance) {
  183. const program = ensureProgram(instance);
  184. return program && !!program.getProjectReferences;
  185. }
  186. exports.supportsProjectReferences = supportsProjectReferences;
  187. function isUsingProjectReferences(instance) {
  188. if (instance.loaderOptions.projectReferences &&
  189. supportsProjectReferences(instance)) {
  190. const program = ensureProgram(instance);
  191. return Boolean(program && program.getProjectReferences());
  192. }
  193. return false;
  194. }
  195. exports.isUsingProjectReferences = isUsingProjectReferences;
  196. /**
  197. * Gets the project reference for a file from the cache if it exists,
  198. * or gets it from TypeScript and caches it otherwise.
  199. */
  200. function getAndCacheProjectReference(filePath, instance) {
  201. const file = instance.files.get(filePath);
  202. if (file !== undefined && file.projectReference) {
  203. return file.projectReference.project;
  204. }
  205. const projectReference = getProjectReferenceForFile(filePath, instance);
  206. if (file !== undefined) {
  207. file.projectReference = { project: projectReference };
  208. }
  209. return projectReference;
  210. }
  211. exports.getAndCacheProjectReference = getAndCacheProjectReference;
  212. function getResolvedProjectReferences(program) {
  213. const getProjectReferences = program.getResolvedProjectReferences ||
  214. program.getProjectReferences;
  215. if (getProjectReferences) {
  216. return getProjectReferences();
  217. }
  218. return;
  219. }
  220. function getProjectReferenceForFile(filePath, instance) {
  221. if (isUsingProjectReferences(instance)) {
  222. const program = ensureProgram(instance);
  223. return (program &&
  224. getResolvedProjectReferences(program).find(ref => (ref &&
  225. ref.commandLine.fileNames.some(file => path.normalize(file) === filePath)) ||
  226. false));
  227. }
  228. return;
  229. }
  230. function validateSourceMapOncePerProject(instance, loader, jsFileName, project) {
  231. const { projectsMissingSourceMaps = new Set() } = instance;
  232. if (!projectsMissingSourceMaps.has(project.sourceFile.fileName)) {
  233. instance.projectsMissingSourceMaps = projectsMissingSourceMaps;
  234. projectsMissingSourceMaps.add(project.sourceFile.fileName);
  235. const mapFileName = jsFileName + '.map';
  236. if (!instance.compiler.sys.fileExists(mapFileName)) {
  237. const [relativeJSPath, relativeProjectConfigPath] = [
  238. path.relative(loader.rootContext, jsFileName),
  239. path.relative(loader.rootContext, project.sourceFile.fileName)
  240. ];
  241. loader.emitWarning(new Error('Could not find source map file for referenced project output ' +
  242. `${relativeJSPath}. Ensure the 'sourceMap' compiler option ` +
  243. `is enabled in ${relativeProjectConfigPath} to ensure Webpack ` +
  244. 'can map project references to the appropriate source files.'));
  245. }
  246. }
  247. }
  248. exports.validateSourceMapOncePerProject = validateSourceMapOncePerProject;
  249. /**
  250. * Gets the output JS file path for an input file governed by a composite project.
  251. * Pulls from the cache if it exists; computes and caches the result otherwise.
  252. */
  253. function getAndCacheOutputJSFileName(inputFileName, projectReference, instance) {
  254. const file = instance.files.get(inputFileName);
  255. if (file && file.projectReference && file.projectReference.outputFileName) {
  256. return file.projectReference.outputFileName;
  257. }
  258. const outputFileName = getOutputJavaScriptFileName(inputFileName, projectReference);
  259. if (file !== undefined) {
  260. file.projectReference = file.projectReference || {
  261. project: projectReference
  262. };
  263. file.projectReference.outputFileName = outputFileName;
  264. }
  265. return outputFileName;
  266. }
  267. exports.getAndCacheOutputJSFileName = getAndCacheOutputJSFileName;
  268. // Adapted from https://github.com/Microsoft/TypeScript/blob/45101491c0b077c509b25830ef0ee5f85b293754/src/compiler/tsbuild.ts#L305
  269. function getOutputJavaScriptFileName(inputFileName, projectReference) {
  270. const { options } = projectReference.commandLine;
  271. const projectDirectory = options.rootDir || path.dirname(projectReference.sourceFile.fileName);
  272. const relativePath = path.relative(projectDirectory, inputFileName);
  273. const outputPath = path.resolve(options.outDir || projectDirectory, relativePath);
  274. const newExtension = constants.jsonRegex.test(inputFileName)
  275. ? '.json'
  276. : constants.tsxRegex.test(inputFileName) &&
  277. options.jsx === typescript.JsxEmit.Preserve
  278. ? '.jsx'
  279. : '.js';
  280. return outputPath.replace(constants.extensionRegex, newExtension);
  281. }