UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

134 lines (132 loc) 6.31 kB
import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; // Based on https://github.com/ember-cli/babel-remove-types/blob/fc3be010e99c4f4926fd70d00242d6777ab1b8d7/src/index.ts // Converted to use Babel standalone, with added TSX support import * as Babel from '@babel/standalone'; import prettier from 'prettier/standalone'; import prettierPluginEstree from 'prettier/plugins/estree'; import parserBabel from 'prettier/parser-babel'; /** * Strips TypeScript types and decorators from code (including React in TSX), * preserving blank lines and optionally formatting with Prettier. * * @param code - The source code string to transform. * @param filename - The name of the file (e.g. "foo.ts" or "Foo.tsx"). * Determines whether TSX parsing is enabled. * @param prettierConfig - `true` for default formatting, `false` to skip, * or a Prettier options object to customize. * @returns The transformed (and optionally formatted) code. */ export function removeTypes(_x) { return _removeTypes.apply(this, arguments); } function _removeTypes() { _removeTypes = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(code) { var filename, prettierConfig, removeComments, isTSX, transformed, fixed, standardPrettierOptions, mergedPrettierOptions, _args = arguments; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: filename = _args.length > 1 && _args[1] !== undefined ? _args[1] : 'file.ts'; prettierConfig = _args.length > 2 && _args[2] !== undefined ? _args[2] : true; // Babel collapses newlines all over the place, which messes with the formatting of almost any // code you pass to it. To preserve the formatting, we go through and mark all the empty lines // in the code string *before* transforming it. This allows us to go back through after the // transformation re-insert the empty lines in the correct place relative to the new code that // has been generated. code = code.replace(/\n\n+/g, '/* ___NEWLINE___ */\n'); // When removing TS-specific constructs (e.g. interfaces), we want to make sure we also remove // any comments that are associated with those constructs, since otherwise we'll be left with // comments that refer to something that isn't actually there. // Credit to https://github.com/cyco130/detype for figuring out this very useful pattern removeComments = { enter: function enter(nodePath) { if (!nodePath.node.leadingComments) { return; } for (var i = nodePath.node.leadingComments.length - 1; i >= 0; i -= 1) { var comment = nodePath.node.leadingComments[i]; if (code.slice(comment.end).match(/^\s*\n\s*\n/) || comment.value.includes('___NEWLINE___')) { // There is at least one empty line between the comment and the TypeScript specific construct // We should keep this comment and those before it break; } comment.value = '___REMOVE_ME___'; } } }; isTSX = /\.tsx$/i.test(filename); transformed = Babel.transform(code, { filename: filename, plugins: [{ name: 'comment-remover', visitor: { TSTypeAliasDeclaration: removeComments, TSInterfaceDeclaration: removeComments, TSDeclareFunction: removeComments, TSDeclareMethod: removeComments, TSImportType: removeComments, TSModuleDeclaration: removeComments } }, ['transform-typescript', { onlyRemoveTypeImports: true, isTSX: isTSX, allExtensions: true }], ['proposal-decorators', { legacy: true }]], generatorOpts: { retainLines: true, shouldPrintComment: function shouldPrintComment(c) { return c !== '___REMOVE_ME___'; } } }); if (!(!transformed || !transformed.code)) { _context.next = 8; break; } throw new Error('There was an issue with the Babel transform.'); case 8: fixed = transformed.code.replace(/\/\* ___NEWLINE___ \*\//g, '\n'); // If the user has *explicitly* passed `false` here, it means they do not want us to run Prettier // at all, so we bail here. if (!(prettierConfig === false)) { _context.next = 11; break; } return _context.abrupt("return", fixed); case 11: standardPrettierOptions = { parser: 'babel', singleQuote: true, plugins: [prettierPluginEstree, parserBabel] }; // If `prettierConfig` is *explicitly* true (as opposed to truthy), it means the user has opted in // to default behavior either explicitly or implicitly. Either way, we run basic Prettier on it. if (!(prettierConfig === true)) { _context.next = 14; break; } return _context.abrupt("return", prettier.format(fixed, standardPrettierOptions)); case 14: // If we've made it here, the user has passed their own Prettier options so we merge it with ours // and let theirs overwrite any of the default settings. mergedPrettierOptions = _objectSpread(_objectSpread(_objectSpread({}, standardPrettierOptions), prettierConfig), {}, { plugins: standardPrettierOptions.plugins }); return _context.abrupt("return", prettier.format(fixed, mergedPrettierOptions)); case 16: case "end": return _context.stop(); } }, _callee); })); return _removeTypes.apply(this, arguments); }