@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
134 lines (132 loc) • 6.31 kB
JavaScript
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);
}