UNPKG

33.4 kBJavaScriptView Raw
1'use strict';
2
3var path = require('path');
4var pluginutils = require('@rollup/pluginutils');
5var defaultTs = require('typescript');
6var resolveId = require('resolve');
7var fs = require('fs');
8
9function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
11function _interopNamespace(e) {
12 if (e && e.__esModule) return e;
13 var n = Object.create(null);
14 if (e) {
15 Object.keys(e).forEach(function (k) {
16 if (k !== 'default') {
17 var d = Object.getOwnPropertyDescriptor(e, k);
18 Object.defineProperty(n, k, d.get ? d : {
19 enumerable: true,
20 get: function () {
21 return e[k];
22 }
23 });
24 }
25 });
26 }
27 n['default'] = e;
28 return Object.freeze(n);
29}
30
31var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
32var defaultTs__namespace = /*#__PURE__*/_interopNamespace(defaultTs);
33var resolveId__default = /*#__PURE__*/_interopDefaultLegacy(resolveId);
34var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
35
36/**
37 * Create a format diagnostics host to use with the Typescript type checking APIs.
38 * Typescript hosts are used to represent the user's system,
39 * with an API for checking case sensitivity etc.
40 * @param compilerOptions Typescript compiler options. Affects functions such as `getNewLine`.
41 * @see https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API
42 */
43function createFormattingHost(ts, compilerOptions) {
44 return {
45 /** Returns the compiler options for the project. */
46 getCompilationSettings: () => compilerOptions,
47 /** Returns the current working directory. */
48 getCurrentDirectory: () => process.cwd(),
49 /** Returns the string that corresponds with the selected `NewLineKind`. */
50 getNewLine() {
51 switch (compilerOptions.newLine) {
52 case ts.NewLineKind.CarriageReturnLineFeed:
53 return '\r\n';
54 case ts.NewLineKind.LineFeed:
55 return '\n';
56 default:
57 return ts.sys.newLine;
58 }
59 },
60 /** Returns a lower case name on case insensitive systems, otherwise the original name. */
61 getCanonicalFileName: (fileName) => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase()
62 };
63}
64
65/**
66 * Create a helper for resolving modules using Typescript.
67 * @param host Typescript host that extends `ModuleResolutionHost`
68 * with methods for sanitizing filenames and getting compiler options.
69 */
70function createModuleResolver(ts, host) {
71 const compilerOptions = host.getCompilationSettings();
72 const cache = ts.createModuleResolutionCache(process.cwd(), host.getCanonicalFileName, compilerOptions);
73 const moduleHost = Object.assign(Object.assign({}, ts.sys), host);
74 return (moduleName, containingFile) => {
75 const resolved = ts.nodeModuleNameResolver(moduleName, containingFile, compilerOptions, moduleHost, cache);
76 return resolved.resolvedModule;
77 };
78}
79
80/*! *****************************************************************************
81Copyright (c) Microsoft Corporation.
82
83Permission to use, copy, modify, and/or distribute this software for any
84purpose with or without fee is hereby granted.
85
86THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
87REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
88AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
89INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
90LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
91OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
92PERFORMANCE OF THIS SOFTWARE.
93***************************************************************************** */
94
95function __rest(s, e) {
96 var t = {};
97 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
98 t[p] = s[p];
99 if (s != null && typeof Object.getOwnPropertySymbols === "function")
100 for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
101 if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
102 t[p[i]] = s[p[i]];
103 }
104 return t;
105}
106
107const resolveIdAsync = (file, opts) => new Promise((fulfil, reject) => resolveId__default['default'](file, opts, (err, contents) => (err || typeof contents === 'undefined' ? reject(err) : fulfil(contents))));
108/**
109 * Returns code asynchronously for the tslib helper library.
110 */
111function getTsLibPath() {
112 return resolveIdAsync('tslib/tslib.es6.js', { basedir: __dirname });
113}
114
115/**
116 * Separate the Rollup plugin options from the Typescript compiler options,
117 * and normalize the Rollup options.
118 * @returns Object with normalized options:
119 * - `filter`: Checks if a file should be included.
120 * - `tsconfig`: Path to a tsconfig, or directive to ignore tsconfig.
121 * - `compilerOptions`: Custom Typescript compiler options that override tsconfig.
122 * - `typescript`: Instance of Typescript library (possibly custom).
123 * - `tslib`: ESM code from the tslib helper library (possibly custom).
124 */
125function getPluginOptions(options) {
126 const { cacheDir, exclude, include, transformers, tsconfig, tslib, typescript } = options, compilerOptions = __rest(options, ["cacheDir", "exclude", "include", "transformers", "tsconfig", "tslib", "typescript"]);
127 const filter = pluginutils.createFilter(include || ['*.ts+(|x)', '**/*.ts+(|x)'], exclude);
128 return {
129 cacheDir,
130 filter,
131 tsconfig,
132 compilerOptions: compilerOptions,
133 typescript: typescript || defaultTs__namespace,
134 tslib: tslib || getTsLibPath(),
135 transformers
136 };
137}
138
139/**
140 * Converts a Typescript type error into an equivalent Rollup warning object.
141 */
142function diagnosticToWarning(ts, host, diagnostic) {
143 const pluginCode = `TS${diagnostic.code}`;
144 const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
145 // Build a Rollup warning object from the diagnostics object.
146 const warning = {
147 pluginCode,
148 message: `@rollup/plugin-typescript ${pluginCode}: ${message}`
149 };
150 if (diagnostic.file) {
151 // Add information about the file location
152 const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
153 warning.loc = {
154 column: character + 1,
155 line: line + 1,
156 file: diagnostic.file.fileName
157 };
158 if (host) {
159 // Extract a code frame from Typescript
160 const formatted = ts.formatDiagnosticsWithColorAndContext([diagnostic], host);
161 // Typescript only exposes this formatter as a string prefixed with the flattened message.
162 // We need to remove it here since Rollup treats the properties as separate parts.
163 let frame = formatted.slice(formatted.indexOf(message) + message.length);
164 const newLine = host.getNewLine();
165 if (frame.startsWith(newLine)) {
166 frame = frame.slice(frame.indexOf(newLine) + newLine.length);
167 }
168 warning.frame = frame;
169 }
170 }
171 return warning;
172}
173
174const DEFAULT_COMPILER_OPTIONS = {
175 module: 'esnext',
176 skipLibCheck: true
177};
178const FORCED_COMPILER_OPTIONS = {
179 // Always use tslib
180 noEmitHelpers: true,
181 importHelpers: true,
182 // Typescript needs to emit the code for us to work with
183 noEmit: false,
184 emitDeclarationOnly: false,
185 // Preventing Typescript from resolving code may break compilation
186 noResolve: false
187};
188
189/* eslint-disable no-param-reassign */
190const DIRECTORY_PROPS = ['outDir', 'declarationDir'];
191/**
192 * Mutates the compiler options to convert paths from relative to absolute.
193 * This should be used with compiler options passed through the Rollup plugin options,
194 * not those found from loading a tsconfig.json file.
195 * @param compilerOptions Compiler options to _mutate_.
196 * @param relativeTo Paths are resolved relative to this path.
197 */
198function makePathsAbsolute(compilerOptions, relativeTo) {
199 for (const pathProp of DIRECTORY_PROPS) {
200 if (compilerOptions[pathProp]) {
201 compilerOptions[pathProp] = path.resolve(relativeTo, compilerOptions[pathProp]);
202 }
203 }
204}
205/**
206 * Mutates the compiler options to normalize some values for Rollup.
207 * @param compilerOptions Compiler options to _mutate_.
208 * @returns True if the source map compiler option was not initially set.
209 */
210function normalizeCompilerOptions(ts, compilerOptions) {
211 let autoSetSourceMap = false;
212 if (compilerOptions.inlineSourceMap) {
213 // Force separate source map files for Rollup to work with.
214 compilerOptions.sourceMap = true;
215 compilerOptions.inlineSourceMap = false;
216 }
217 else if (typeof compilerOptions.sourceMap !== 'boolean') {
218 // Default to using source maps.
219 // If the plugin user sets sourceMap to false we keep that option.
220 compilerOptions.sourceMap = true;
221 // Using inlineSources to make sure typescript generate source content
222 // instead of source path.
223 compilerOptions.inlineSources = true;
224 autoSetSourceMap = true;
225 }
226 switch (compilerOptions.module) {
227 case ts.ModuleKind.ES2015:
228 case ts.ModuleKind.ESNext:
229 case ts.ModuleKind.CommonJS:
230 // OK module type
231 return autoSetSourceMap;
232 case ts.ModuleKind.None:
233 case ts.ModuleKind.AMD:
234 case ts.ModuleKind.UMD:
235 case ts.ModuleKind.System: {
236 // Invalid module type
237 const moduleType = ts.ModuleKind[compilerOptions.module];
238 throw new Error(`@rollup/plugin-typescript: The module kind should be 'ES2015' or 'ESNext, found: '${moduleType}'`);
239 }
240 default:
241 // Unknown or unspecified module type, force ESNext
242 compilerOptions.module = ts.ModuleKind.ESNext;
243 }
244 return autoSetSourceMap;
245}
246
247/**
248 * Finds the path to the tsconfig file relative to the current working directory.
249 * @param relativePath Relative tsconfig path given by the user.
250 * If `false` is passed, then a null path is returned.
251 * @returns The absolute path, or null if the file does not exist.
252 */
253function getTsConfigPath(ts, relativePath) {
254 if (relativePath === false)
255 return null;
256 // Resolve path to file. `tsConfigOption` defaults to 'tsconfig.json'.
257 const tsConfigPath = path.resolve(process.cwd(), relativePath || 'tsconfig.json');
258 if (!ts.sys.fileExists(tsConfigPath)) {
259 if (relativePath) {
260 // If an explicit path was provided but no file was found, throw
261 throw new Error(`Could not find specified tsconfig.json at ${tsConfigPath}`);
262 }
263 else {
264 return null;
265 }
266 }
267 return tsConfigPath;
268}
269/**
270 * Tries to read the tsconfig file at `tsConfigPath`.
271 * @param tsConfigPath Absolute path to tsconfig JSON file.
272 * @param explicitPath If true, the path was set by the plugin user.
273 * If false, the path was computed automatically.
274 */
275function readTsConfigFile(ts, tsConfigPath) {
276 const { config, error } = ts.readConfigFile(tsConfigPath, (path) => fs.readFileSync(path, 'utf8'));
277 if (error) {
278 throw Object.assign(Error(), diagnosticToWarning(ts, null, error));
279 }
280 return config || {};
281}
282/**
283 * Returns true if any of the `compilerOptions` contain an enum value (i.e.: ts.ScriptKind) rather than a string.
284 * This indicates that the internal CompilerOptions type is used rather than the JsonCompilerOptions.
285 */
286function containsEnumOptions(compilerOptions) {
287 const enums = [
288 'module',
289 'target',
290 'jsx',
291 'moduleResolution',
292 'newLine'
293 ];
294 return enums.some((prop) => prop in compilerOptions && typeof compilerOptions[prop] === 'number');
295}
296const configCache = new Map();
297/**
298 * Parse the Typescript config to use with the plugin.
299 * @param ts Typescript library instance.
300 * @param tsconfig Path to the tsconfig file, or `false` to ignore the file.
301 * @param compilerOptions Options passed to the plugin directly for Typescript.
302 *
303 * @returns Parsed tsconfig.json file with some important properties:
304 * - `options`: Parsed compiler options.
305 * - `fileNames` Type definition files that should be included in the build.
306 * - `errors`: Any errors from parsing the config file.
307 */
308function parseTypescriptConfig(ts, tsconfig, compilerOptions) {
309 /* eslint-disable no-undefined */
310 const cwd = process.cwd();
311 makePathsAbsolute(compilerOptions, cwd);
312 let parsedConfig;
313 // Resolve path to file. If file is not found, pass undefined path to `parseJsonConfigFileContent`.
314 // eslint-disable-next-line no-undefined
315 const tsConfigPath = getTsConfigPath(ts, tsconfig) || undefined;
316 const tsConfigFile = tsConfigPath ? readTsConfigFile(ts, tsConfigPath) : {};
317 const basePath = tsConfigPath ? path.dirname(tsConfigPath) : cwd;
318 // If compilerOptions has enums, it represents an CompilerOptions object instead of parsed JSON.
319 // This determines where the data is passed to the parser.
320 if (containsEnumOptions(compilerOptions)) {
321 parsedConfig = ts.parseJsonConfigFileContent(Object.assign(Object.assign({}, tsConfigFile), { compilerOptions: Object.assign(Object.assign({}, DEFAULT_COMPILER_OPTIONS), tsConfigFile.compilerOptions) }), ts.sys, basePath, Object.assign(Object.assign({}, compilerOptions), FORCED_COMPILER_OPTIONS), tsConfigPath, undefined, undefined, configCache);
322 }
323 else {
324 parsedConfig = ts.parseJsonConfigFileContent(Object.assign(Object.assign({}, tsConfigFile), { compilerOptions: Object.assign(Object.assign(Object.assign({}, DEFAULT_COMPILER_OPTIONS), tsConfigFile.compilerOptions), compilerOptions) }), ts.sys, basePath, FORCED_COMPILER_OPTIONS, tsConfigPath, undefined, undefined, configCache);
325 }
326 const autoSetSourceMap = normalizeCompilerOptions(ts, parsedConfig.options);
327 return Object.assign(Object.assign({}, parsedConfig), { autoSetSourceMap });
328}
329/**
330 * If errors are detected in the parsed options,
331 * display all of them as warnings then emit an error.
332 */
333function emitParsedOptionsErrors(ts, context, parsedOptions) {
334 if (parsedOptions.errors.length > 0) {
335 parsedOptions.errors.forEach((error) => context.warn(diagnosticToWarning(ts, null, error)));
336 context.error(`@rollup/plugin-typescript: Couldn't process compiler options`);
337 }
338}
339
340/**
341 * Validate that the `compilerOptions.sourceMap` option matches `outputOptions.sourcemap`.
342 * @param context Rollup plugin context used to emit warnings.
343 * @param compilerOptions Typescript compiler options.
344 * @param outputOptions Rollup output options.
345 * @param autoSetSourceMap True if the `compilerOptions.sourceMap` property was set to `true`
346 * by the plugin, not the user.
347 */
348function validateSourceMap(context, compilerOptions, outputOptions, autoSetSourceMap) {
349 if (compilerOptions.sourceMap && !outputOptions.sourcemap && !autoSetSourceMap) {
350 context.warn(`@rollup/plugin-typescript: Rollup 'sourcemap' option must be set to generate source maps.`);
351 }
352 else if (!compilerOptions.sourceMap && outputOptions.sourcemap) {
353 context.warn(`@rollup/plugin-typescript: Typescript 'sourceMap' compiler option must be set to generate source maps.`);
354 }
355}
356/**
357 * Validate that the out directory used by Typescript can be controlled by Rollup.
358 * @param context Rollup plugin context used to emit errors.
359 * @param compilerOptions Typescript compiler options.
360 * @param outputOptions Rollup output options.
361 */
362function validatePaths(ts, context, compilerOptions, outputOptions) {
363 if (compilerOptions.out) {
364 context.error(`@rollup/plugin-typescript: Deprecated Typescript compiler option 'out' is not supported. Use 'outDir' instead.`);
365 }
366 else if (compilerOptions.outFile) {
367 context.error(`@rollup/plugin-typescript: Typescript compiler option 'outFile' is not supported. Use 'outDir' instead.`);
368 }
369 for (const dirProperty of DIRECTORY_PROPS) {
370 if (compilerOptions[dirProperty] && outputOptions.dir) {
371 // Checks if the given path lies within Rollup output dir
372 const fromRollupDirToTs = path.relative(outputOptions.dir, compilerOptions[dirProperty]);
373 if (fromRollupDirToTs.startsWith('..')) {
374 context.error(`@rollup/plugin-typescript: Path of Typescript compiler option '${dirProperty}' must be located inside Rollup 'dir' option.`);
375 }
376 }
377 }
378 const tsBuildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(compilerOptions);
379 if (tsBuildInfoPath && compilerOptions.incremental) {
380 if (!outputOptions.dir) {
381 context.error(`@rollup/plugin-typescript: Rollup 'dir' option must be used when Typescript compiler options 'tsBuildInfoFile' or 'incremental' are specified.`);
382 }
383 // Checks if the given path lies within Rollup output dir
384 const fromRollupDirToTs = path.relative(outputOptions.dir, tsBuildInfoPath);
385 if (fromRollupDirToTs.startsWith('..')) {
386 context.error(`@rollup/plugin-typescript: Path of Typescript compiler option 'tsBuildInfoFile' must be located inside Rollup 'dir' option.`);
387 }
388 }
389 if (compilerOptions.declaration || compilerOptions.declarationMap || compilerOptions.composite) {
390 if (DIRECTORY_PROPS.every((dirProperty) => !compilerOptions[dirProperty])) {
391 context.error(`@rollup/plugin-typescript: You are using one of Typescript's compiler options 'declaration', 'declarationMap' or 'composite'. ` +
392 `In this case 'outDir' or 'declarationDir' must be specified to generate declaration files.`);
393 }
394 }
395}
396
397/**
398 * Checks if the given OutputFile represents some code
399 */
400function isCodeOutputFile(name) {
401 return !isMapOutputFile(name) && !name.endsWith('.d.ts');
402}
403/**
404 * Checks if the given OutputFile represents some source map
405 */
406function isMapOutputFile(name) {
407 return name.endsWith('.map');
408}
409/**
410 * Returns the content of a filename either from the current
411 * typescript compiler instance or from the cached content.
412 * @param fileName The filename for the contents to retrieve
413 * @param emittedFiles The files emitted in the current typescript instance
414 * @param tsCache A cache to files cached by Typescript
415 */
416function getEmittedFile(fileName, emittedFiles, tsCache) {
417 let code;
418 if (fileName) {
419 if (emittedFiles.has(fileName)) {
420 code = emittedFiles.get(fileName);
421 }
422 else {
423 code = tsCache.getCached(fileName);
424 }
425 }
426 return code;
427}
428/**
429 * Finds the corresponding emitted Javascript files for a given Typescript file.
430 * @param id Path to the Typescript file.
431 * @param emittedFiles Map of file names to source code,
432 * containing files emitted by the Typescript compiler.
433 */
434function findTypescriptOutput(ts, parsedOptions, id, emittedFiles, tsCache) {
435 const emittedFileNames = ts.getOutputFileNames(parsedOptions, id, !ts.sys.useCaseSensitiveFileNames);
436 const codeFile = emittedFileNames.find(isCodeOutputFile);
437 const mapFile = emittedFileNames.find(isMapOutputFile);
438 return {
439 code: getEmittedFile(codeFile, emittedFiles, tsCache),
440 map: getEmittedFile(mapFile, emittedFiles, tsCache),
441 declarations: emittedFileNames.filter((name) => name !== codeFile && name !== mapFile)
442 };
443}
444
445// `Cannot compile modules into 'es6' when targeting 'ES5' or lower.`
446const CANNOT_COMPILE_ESM = 1204;
447/**
448 * Emit a Rollup warning or error for a Typescript type error.
449 */
450function emitDiagnostic(ts, context, host, diagnostic) {
451 if (diagnostic.code === CANNOT_COMPILE_ESM)
452 return;
453 const { noEmitOnError } = host.getCompilationSettings();
454 // Build a Rollup warning object from the diagnostics object.
455 const warning = diagnosticToWarning(ts, host, diagnostic);
456 // Errors are fatal. Otherwise emit warnings.
457 if (noEmitOnError && diagnostic.category === ts.DiagnosticCategory.Error) {
458 context.error(warning);
459 }
460 else {
461 context.warn(warning);
462 }
463}
464function buildDiagnosticReporter(ts, context, host) {
465 return function reportDiagnostics(diagnostic) {
466 emitDiagnostic(ts, context, host, diagnostic);
467 };
468}
469
470/**
471 * Merges all received custom transformer definitions into a single CustomTransformers object
472 */
473function mergeTransformers(builder, ...input) {
474 // List of all transformer stages
475 const transformerTypes = ['after', 'afterDeclarations', 'before'];
476 const accumulator = {
477 after: [],
478 afterDeclarations: [],
479 before: []
480 };
481 let program;
482 let typeChecker;
483 input.forEach((transformers) => {
484 if (!transformers) {
485 // Skip empty arguments lists
486 return;
487 }
488 transformerTypes.forEach((stage) => {
489 getTransformers(transformers[stage]).forEach((transformer) => {
490 if (!transformer) {
491 // Skip empty
492 return;
493 }
494 if ('type' in transformer) {
495 if (typeof transformer.factory === 'function') {
496 // Allow custom factories to grab the extra information required
497 program = program || builder.getProgram();
498 typeChecker = typeChecker || program.getTypeChecker();
499 let factory;
500 if (transformer.type === 'program') {
501 program = program || builder.getProgram();
502 factory = transformer.factory(program);
503 }
504 else {
505 program = program || builder.getProgram();
506 typeChecker = typeChecker || program.getTypeChecker();
507 factory = transformer.factory(typeChecker);
508 }
509 // Forward the requested reference to the custom transformer factory
510 if (factory) {
511 accumulator[stage].push(factory);
512 }
513 }
514 }
515 else {
516 // Add normal transformer factories as is
517 accumulator[stage].push(transformer);
518 }
519 });
520 });
521 });
522 return accumulator;
523}
524function getTransformers(transformers) {
525 return transformers || [];
526}
527
528// @see https://github.com/microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json
529var DiagnosticCode;
530(function (DiagnosticCode) {
531 DiagnosticCode[DiagnosticCode["FILE_CHANGE_DETECTED"] = 6032] = "FILE_CHANGE_DETECTED";
532 DiagnosticCode[DiagnosticCode["FOUND_1_ERROR_WATCHING_FOR_FILE_CHANGES"] = 6193] = "FOUND_1_ERROR_WATCHING_FOR_FILE_CHANGES";
533 DiagnosticCode[DiagnosticCode["FOUND_N_ERRORS_WATCHING_FOR_FILE_CHANGES"] = 6194] = "FOUND_N_ERRORS_WATCHING_FOR_FILE_CHANGES";
534})(DiagnosticCode || (DiagnosticCode = {}));
535function createDeferred(timeout) {
536 let promise;
537 let resolve = () => { };
538 if (timeout) {
539 promise = Promise.race([
540 new Promise((r) => setTimeout(r, timeout, true)),
541 new Promise((r) => (resolve = r))
542 ]);
543 }
544 else {
545 promise = new Promise((r) => (resolve = r));
546 }
547 return { promise, resolve };
548}
549/**
550 * Typescript watch program helper to sync Typescript watch status with Rollup hooks.
551 */
552class WatchProgramHelper {
553 constructor() {
554 this._startDeferred = null;
555 this._finishDeferred = null;
556 }
557 watch(timeout = 1000) {
558 // Race watcher start promise against a timeout in case Typescript and Rollup change detection is not in sync.
559 this._startDeferred = createDeferred(timeout);
560 this._finishDeferred = createDeferred();
561 }
562 handleStatus(diagnostic) {
563 // Fullfil deferred promises by Typescript diagnostic message codes.
564 if (diagnostic.category === defaultTs.DiagnosticCategory.Message) {
565 switch (diagnostic.code) {
566 case DiagnosticCode.FILE_CHANGE_DETECTED:
567 this.resolveStart();
568 break;
569 case DiagnosticCode.FOUND_1_ERROR_WATCHING_FOR_FILE_CHANGES:
570 case DiagnosticCode.FOUND_N_ERRORS_WATCHING_FOR_FILE_CHANGES:
571 this.resolveFinish();
572 break;
573 }
574 }
575 }
576 resolveStart() {
577 if (this._startDeferred) {
578 this._startDeferred.resolve(false);
579 this._startDeferred = null;
580 }
581 }
582 resolveFinish() {
583 if (this._finishDeferred) {
584 this._finishDeferred.resolve(false);
585 this._finishDeferred = null;
586 }
587 }
588 async wait() {
589 var _a;
590 if (this._startDeferred) {
591 const timeout = await this._startDeferred.promise;
592 // If there is no file change detected by Typescript skip deferred promises.
593 if (timeout) {
594 this._startDeferred = null;
595 this._finishDeferred = null;
596 }
597 await ((_a = this._finishDeferred) === null || _a === void 0 ? void 0 : _a.promise);
598 }
599 }
600}
601/**
602 * Create a language service host to use with the Typescript compiler & type checking APIs.
603 * Typescript hosts are used to represent the user's system,
604 * with an API for reading files, checking directories and case sensitivity etc.
605 * @see https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API
606 */
607function createWatchHost(ts, context, { formatHost, parsedOptions, writeFile, status, resolveModule, transformers }) {
608 const createProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram;
609 const baseHost = ts.createWatchCompilerHost(parsedOptions.fileNames, parsedOptions.options, ts.sys, createProgram, buildDiagnosticReporter(ts, context, formatHost), status, parsedOptions.projectReferences);
610 return Object.assign(Object.assign({}, baseHost), {
611 /** Override the created program so an in-memory emit is used */
612 afterProgramCreate(program) {
613 const origEmit = program.emit;
614 // eslint-disable-next-line no-param-reassign
615 program.emit = (targetSourceFile, _, ...args) => origEmit(targetSourceFile, writeFile,
616 // cancellationToken
617 args[0],
618 // emitOnlyDtsFiles
619 args[1], mergeTransformers(program, transformers, args[2]));
620 return baseHost.afterProgramCreate(program);
621 },
622 /** Add helper to deal with module resolution */
623 resolveModuleNames(moduleNames, containingFile) {
624 return moduleNames.map((moduleName) => resolveModule(moduleName, containingFile));
625 } });
626}
627function createWatchProgram(ts, context, options) {
628 return ts.createWatchProgram(createWatchHost(ts, context, options));
629}
630
631/** Creates the folders needed given a path to a file to be saved*/
632const createFileFolder = (filePath) => {
633 const folderPath = path__default['default'].dirname(filePath);
634 fs__default['default'].mkdirSync(folderPath, { recursive: true });
635};
636class TSCache {
637 constructor(cacheFolder = '.rollup.cache') {
638 this._cacheFolder = cacheFolder;
639 }
640 /** Returns the path to the cached file */
641 cachedFilename(fileName) {
642 return path__default['default'].join(this._cacheFolder, fileName.replace(/^([A-Z]+):/, '$1'));
643 }
644 /** Emits a file in the cache folder */
645 cacheCode(fileName, code) {
646 const cachedPath = this.cachedFilename(fileName);
647 createFileFolder(cachedPath);
648 fs__default['default'].writeFileSync(cachedPath, code);
649 }
650 /** Checks if a file is in the cache */
651 isCached(fileName) {
652 return fs__default['default'].existsSync(this.cachedFilename(fileName));
653 }
654 /** Read a file from the cache given the output name*/
655 getCached(fileName) {
656 let code;
657 if (this.isCached(fileName)) {
658 code = fs__default['default'].readFileSync(this.cachedFilename(fileName), { encoding: 'utf-8' });
659 }
660 return code;
661 }
662}
663
664function typescript(options = {}) {
665 const { cacheDir, compilerOptions, filter, transformers, tsconfig, tslib, typescript: ts } = getPluginOptions(options);
666 const tsCache = new TSCache(cacheDir);
667 const emittedFiles = new Map();
668 const watchProgramHelper = new WatchProgramHelper();
669 const parsedOptions = parseTypescriptConfig(ts, tsconfig, compilerOptions);
670 parsedOptions.fileNames = parsedOptions.fileNames.filter(filter);
671 const formatHost = createFormattingHost(ts, parsedOptions.options);
672 const resolveModule = createModuleResolver(ts, formatHost);
673 let program = null;
674 function normalizePath(fileName) {
675 return fileName.split(path.win32.sep).join(path.posix.sep);
676 }
677 return {
678 name: 'typescript',
679 buildStart() {
680 emitParsedOptionsErrors(ts, this, parsedOptions);
681 // Fixes a memory leak https://github.com/rollup/plugins/issues/322
682 if (!program) {
683 program = createWatchProgram(ts, this, {
684 formatHost,
685 resolveModule,
686 parsedOptions,
687 writeFile(fileName, data) {
688 if (parsedOptions.options.composite || parsedOptions.options.incremental) {
689 tsCache.cacheCode(fileName, data);
690 }
691 emittedFiles.set(fileName, data);
692 },
693 status(diagnostic) {
694 watchProgramHelper.handleStatus(diagnostic);
695 },
696 transformers
697 });
698 }
699 },
700 watchChange(id) {
701 if (!filter(id))
702 return;
703 watchProgramHelper.watch();
704 },
705 buildEnd() {
706 if (this.meta.watchMode !== true) {
707 // ESLint doesn't understand optional chaining
708 // eslint-disable-next-line
709 program === null || program === void 0 ? void 0 : program.close();
710 }
711 },
712 renderStart(outputOptions) {
713 validateSourceMap(this, parsedOptions.options, outputOptions, parsedOptions.autoSetSourceMap);
714 validatePaths(ts, this, parsedOptions.options, outputOptions);
715 },
716 resolveId(importee, importer) {
717 if (importee === 'tslib') {
718 return tslib;
719 }
720 if (!importer)
721 return null;
722 // Convert path from windows separators to posix separators
723 const containingFile = normalizePath(importer);
724 const resolved = resolveModule(importee, containingFile);
725 if (resolved) {
726 if (resolved.extension === '.d.ts')
727 return null;
728 return path.normalize(resolved.resolvedFileName);
729 }
730 return null;
731 },
732 async load(id) {
733 if (!filter(id))
734 return null;
735 await watchProgramHelper.wait();
736 const fileName = normalizePath(id);
737 if (!parsedOptions.fileNames.includes(fileName)) {
738 // Discovered new file that was not known when originally parsing the TypeScript config
739 parsedOptions.fileNames.push(fileName);
740 }
741 const output = findTypescriptOutput(ts, parsedOptions, id, emittedFiles, tsCache);
742 return output.code != null ? output : null;
743 },
744 generateBundle(outputOptions) {
745 parsedOptions.fileNames.forEach((fileName) => {
746 const output = findTypescriptOutput(ts, parsedOptions, fileName, emittedFiles, tsCache);
747 output.declarations.forEach((id) => {
748 const code = getEmittedFile(id, emittedFiles, tsCache);
749 let baseDir = outputOptions.dir;
750 if (!baseDir && tsconfig) {
751 baseDir = tsconfig.substring(0, tsconfig.lastIndexOf('/'));
752 }
753 if (!code || !baseDir)
754 return;
755 this.emitFile({
756 type: 'asset',
757 fileName: normalizePath(path.relative(baseDir, id)),
758 source: code
759 });
760 });
761 });
762 const tsBuildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(parsedOptions.options);
763 if (tsBuildInfoPath) {
764 const tsBuildInfoSource = emittedFiles.get(tsBuildInfoPath);
765 // https://github.com/rollup/plugins/issues/681
766 if (tsBuildInfoSource) {
767 this.emitFile({
768 type: 'asset',
769 fileName: normalizePath(path.relative(outputOptions.dir, tsBuildInfoPath)),
770 source: tsBuildInfoSource
771 });
772 }
773 }
774 }
775 };
776}
777
778module.exports = typescript;