From f532bf2b495a19540e45d9ed32a6693fca8bceea Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 19 Nov 2020 08:58:56 +0800 Subject: [PATCH] Refactor: Module releated format (#9691) --- src/language-js/print/module.js | 232 +++++++++++++++++- src/language-js/printer-estree.js | 151 ++---------- .../comments/__snapshots__/jsfmt.spec.js.snap | 12 +- 3 files changed, 241 insertions(+), 154 deletions(-) diff --git a/src/language-js/print/module.js b/src/language-js/print/module.js index 107677341..b1b23a6b0 100644 --- a/src/language-js/print/module.js +++ b/src/language-js/print/module.js @@ -1,28 +1,181 @@ "use strict"; const { - builders: { concat, softline, group, indent, join, line, ifBreak }, + builders: { concat, softline, group, indent, join, line, ifBreak, hardline }, } = require("../../document"); +const { printDanglingComments } = require("../../main/comments"); -const { shouldPrintComma } = require("../utils"); +const { + hasDanglingComments, + shouldPrintComma, + needsHardlineAfterDanglingComment, +} = require("../utils"); +const { locStart, hasSameLoc } = require("../loc"); /** * @typedef {import("../../document").Doc} Doc */ +function printImportDeclaration(path, options, print) { + const node = path.getValue(); + const semi = options.semi ? ";" : ""; + /** @type{Doc[]} */ + const parts = []; + + const { importKind } = node; + + parts.push("import"); + + if (importKind && importKind !== "value") { + parts.push(" ", importKind); + } + + parts.push( + printModuleSpecifiers(path, options, print), + printModuleSource(path, options, print), + printImportAssertions(path, options, print) + ); + + parts.push(semi); + + return concat(parts); +} + +function printExportDeclaration(path, options, print) { + const node = path.getValue(); + /** @type{Doc[]} */ + const parts = []; + + const { type, exportKind, declaration } = node; + if (type === "DeclareExportDeclaration") { + parts.push("declare "); + } + + parts.push("export"); + + const isDefaultExport = node.default || type === "ExportDefaultDeclaration"; + if (isDefaultExport) { + parts.push(" default"); + } + + if (hasDanglingComments(node)) { + parts.push( + " ", + printDanglingComments(path, options, /* sameIndent */ true) + ); + + if (needsHardlineAfterDanglingComment(node)) { + parts.push(hardline); + } + } + + if (declaration) { + parts.push(" ", path.call(print, "declaration")); + } else { + parts.push( + exportKind === "type" ? " type" : "", + printModuleSpecifiers(path, options, print), + printModuleSource(path, options, print), + printImportAssertions(path, options, print) + ); + } + + if (shouldExportDeclarationPrintSemi(node, options)) { + parts.push(";"); + } + + return concat(parts); +} + +function printExportAllDeclaration(path, options, print) { + const node = path.getValue(); + let semi = options.semi ? ";" : ""; + /** @type{Doc[]} */ + const parts = []; + + const { type, exportKind, exported } = node; + if (type === "DeclareExportAllDeclaration") { + parts.push("declare "); + semi = ""; + } + + parts.push("export"); + + if (exportKind === "type") { + parts.push(" type"); + } + + parts.push(" *"); + + if (exported) { + parts.push(" as ", path.call(print, "exported")); + } + + parts.push( + printModuleSource(path, options, print), + printImportAssertions(path, options, print), + semi + ); + + return concat(parts); +} + +function shouldExportDeclarationPrintSemi(node, options) { + if (!options.semi) { + return false; + } + + const { type, declaration } = node; + const isDefaultExport = node.default || type === "ExportDefaultDeclaration"; + if (!declaration) { + return true; + } + + const { type: declarationType } = declaration; + if ( + isDefaultExport && + declarationType !== "ClassDeclaration" && + declarationType !== "FunctionDeclaration" && + declarationType !== "TSInterfaceDeclaration" && + declarationType !== "DeclareClass" && + declarationType !== "DeclareFunction" && + declarationType !== "TSDeclareFunction" && + declarationType !== "EnumDeclaration" + ) { + return true; + } + return false; +} + function printModuleSource(path, options, print) { const node = path.getValue(); - return node.source ? concat([" from ", path.call(print, "source")]) : ""; + + if (!node.source) { + return ""; + } + + /** @type{Doc[]} */ + const parts = []; + if (!shouldNotPrintSpecifiers(node, options)) { + parts.push(" from"); + } + parts.push(" ", path.call(print, "source")); + + return concat(parts); } function printModuleSpecifiers(path, options, print) { const node = path.getValue(); + if (shouldNotPrintSpecifiers(node, options)) { + return ""; + } + /** @type{Doc[]} */ - const parts = [node.type === "ImportDeclaration" ? " " : ""]; + const parts = [" "]; if (node.specifiers && node.specifiers.length > 0) { - const standalonesSpecifiers = []; + const standaloneSpecifiers = []; const groupedSpecifiers = []; path.each((specifierPath) => { @@ -33,7 +186,7 @@ function printModuleSpecifiers(path, options, print) { specifierType === "ImportNamespaceSpecifier" || specifierType === "ImportDefaultSpecifier" ) { - standalonesSpecifiers.push(print(specifierPath)); + standaloneSpecifiers.push(print(specifierPath)); } else if ( specifierType === "ExportSpecifier" || specifierType === "ImportSpecifier" @@ -47,16 +200,16 @@ function printModuleSpecifiers(path, options, print) { } }, "specifiers"); - parts.push(join(", ", standalonesSpecifiers)); + parts.push(join(", ", standaloneSpecifiers)); if (groupedSpecifiers.length !== 0) { - if (standalonesSpecifiers.length !== 0) { + if (standaloneSpecifiers.length !== 0) { parts.push(", "); } const canBreak = groupedSpecifiers.length > 1 || - standalonesSpecifiers.length > 0 || + standaloneSpecifiers.length > 0 || node.specifiers.some((node) => node.comments); if (canBreak) { @@ -94,6 +247,23 @@ function printModuleSpecifiers(path, options, print) { return concat(parts); } +function shouldNotPrintSpecifiers(node, options) { + const { type, importKind, source, specifiers } = node; + + if ( + type !== "ImportDeclaration" || + (Array.isArray(specifiers) && specifiers.length > 0) || + importKind === "type" + ) { + return false; + } + + // TODO: check tokens + return !/{\s*}/.test( + options.originalText.slice(locStart(node), locStart(source)) + ); +} + function printImportAssertions(path, options, print) { const node = path.getNode(); if (Array.isArray(node.assertions) && node.assertions.length !== 0) { @@ -108,8 +278,46 @@ function printImportAssertions(path, options, print) { return ""; } +function printModuleSpecifier(path, options, print) { + const node = path.getNode(); + + const { type, importKind } = node; + /** @type{Doc[]} */ + const parts = []; + if (type === "ImportSpecifier" && importKind) { + parts.push(importKind, " "); + } + + const isImport = type.startsWith("Import"); + const leftSideProperty = isImport ? "imported" : "local"; + const rightSideProperty = isImport ? "local" : "exported"; + let left = ""; + let right = ""; + if ( + type === "ExportNamespaceSpecifier" || + type === "ImportNamespaceSpecifier" + ) { + left = "*"; + } else if (node[leftSideProperty]) { + left = path.call(print, leftSideProperty); + } + + if ( + node[rightSideProperty] && + (!node[leftSideProperty] || + // import {a as a} from '.' + !hasSameLoc(node[leftSideProperty], node[rightSideProperty])) + ) { + right = path.call(print, rightSideProperty); + } + + parts.push(left, left && right ? " as " : "", right); + return concat(parts); +} + module.exports = { - printModuleSource, - printModuleSpecifiers, - printImportAssertions, + printImportDeclaration, + printExportDeclaration, + printExportAllDeclaration, + printModuleSpecifier, }; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 4657fa348..44065793b 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -103,7 +103,7 @@ const { shouldFlatten, startsWithNoLookaheadToken, } = require("./utils"); -const { locStart, locEnd, hasSameLoc } = require("./loc"); +const { locStart, locEnd } = require("./loc"); const printMemberChain = require("./print/member-chain"); const printCallArguments = require("./print/call-arguments"); @@ -114,9 +114,10 @@ const { printBindExpressionCallee, } = require("./print/misc"); const { - printModuleSource, - printModuleSpecifiers, - printImportAssertions, + printImportDeclaration, + printExportDeclaration, + printExportAllDeclaration, + printModuleSpecifier, } = require("./print/module"); const printTernaryOperator = require("./print/ternary"); const { @@ -807,93 +808,24 @@ function printPathNoParens(path, options, print, args) { } return concat(parts); } - case "ImportSpecifier": - if (n.importKind) { - parts.push(path.call(print, "importKind"), " "); - } - - parts.push(path.call(print, "imported")); - - if (n.local && !hasSameLoc(n.local, n.imported)) { - parts.push(" as ", path.call(print, "local")); - } - - return concat(parts); - case "ExportSpecifier": { - parts.push(path.call(print, "local")); - - if (n.exported && !hasSameLoc(n.local, n.exported)) { - parts.push(" as ", path.call(print, "exported")); - } - - return concat(parts); - } - case "ImportNamespaceSpecifier": - parts.push("* as "); - parts.push(path.call(print, "local")); - return concat(parts); - case "ImportDefaultSpecifier": - return path.call(print, "local"); case "TSExportAssignment": return concat(["export = ", path.call(print, "expression"), semi]); case "ExportDefaultDeclaration": case "ExportNamedDeclaration": - return printExportDeclaration(path, options, print); case "DeclareExportDeclaration": - return concat(["declare ", printExportDeclaration(path, options, print)]); + return printExportDeclaration(path, options, print); case "ExportAllDeclaration": - parts.push("export"); - - if (n.exportKind === "type") { - parts.push(" type"); - } - - parts.push(" *"); - - if (n.exported) { - parts.push(" as ", path.call(print, "exported")); - } - - parts.push( - printModuleSource(path, options, print), - printImportAssertions(path, options, print), - semi - ); - - return concat(parts); - + case "DeclareExportAllDeclaration": + return printExportAllDeclaration(path, options, print); + case "ImportDeclaration": + return printImportDeclaration(path, options, print); + case "ImportSpecifier": + case "ExportSpecifier": + case "ImportNamespaceSpecifier": case "ExportNamespaceSpecifier": - return concat(["* as ", path.call(print, "exported")]); + case "ImportDefaultSpecifier": case "ExportDefaultSpecifier": - return path.call(print, "exported"); - case "ImportDeclaration": { - parts.push("import"); - - if (n.importKind && n.importKind !== "value") { - parts.push(" ", n.importKind); - } - - if (n.specifiers && n.specifiers.length > 0) { - parts.push(printModuleSpecifiers(path, options, print)); - parts.push(printModuleSource(path, options, print)); - } else if ( - (n.importKind && n.importKind === "type") || - // import {} from 'x' - /{\s*}/.test( - options.originalText.slice(locStart(n), locStart(n.source)) - ) - ) { - parts.push(" {}", printModuleSource(path, options, print)); - } else { - parts.push(" ", path.call(print, "source")); - } - - parts.push(printImportAssertions(path, options, print)); - - parts.push(semi); - - return concat(parts); - } + return printModuleSpecifier(path, options, print); case "ImportAttribute": return concat([path.call(print, "key"), ": ", path.call(print, "value")]); case "Import": @@ -2350,11 +2282,6 @@ function printPathNoParens(path, options, print, args) { ]); case "DeclareVariable": return printFlowDeclaration(path, ["var ", path.call(print, "id"), semi]); - case "DeclareExportAllDeclaration": - return concat([ - "declare export *", - printModuleSource(path, options, print), - ]); case "DeclareOpaqueType": case "OpaqueType": { parts.push( @@ -3808,54 +3735,6 @@ function printReturnType(path, print, options) { return concat(parts); } -function printExportDeclaration(path, options, print) { - const decl = path.getValue(); - - const semi = options.semi ? ";" : ""; - - /** @type{Doc[]} */ - const parts = ["export "]; - - const isDefault = decl.default || decl.type === "ExportDefaultDeclaration"; - - if (isDefault) { - parts.push("default "); - } - - parts.push( - comments.printDanglingComments(path, options, /* sameIndent */ true) - ); - - if (needsHardlineAfterDanglingComment(decl)) { - parts.push(hardline); - } - - if (decl.declaration) { - parts.push(path.call(print, "declaration")); - - if ( - isDefault && - decl.declaration.type !== "ClassDeclaration" && - decl.declaration.type !== "FunctionDeclaration" && - decl.declaration.type !== "TSInterfaceDeclaration" && - decl.declaration.type !== "DeclareClass" && - decl.declaration.type !== "DeclareFunction" && - decl.declaration.type !== "TSDeclareFunction" && - decl.declaration.type !== "EnumDeclaration" - ) { - parts.push(semi); - } - } else { - parts.push(decl.exportKind === "type" ? "type " : ""); - parts.push(printModuleSpecifiers(path, options, print)); - parts.push(printModuleSource(path, options, print)); - parts.push(printImportAssertions(path, options, print)); - parts.push(semi); - } - - return concat(parts); -} - function printFlowDeclaration(path, parts) { const parentExportDecl = getParentExportDeclaration(path); diff --git a/tests/js/comments/__snapshots__/jsfmt.spec.js.snap b/tests/js/comments/__snapshots__/jsfmt.spec.js.snap index 46bb3bfb7..aec6bbbce 100644 --- a/tests/js/comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/js/comments/__snapshots__/jsfmt.spec.js.snap @@ -1175,7 +1175,7 @@ function d() { } new Thing(/* dangling */) Thing(/* dangling */) -export /* dangling */{} +export /* dangling */ {} ================================================================================ `; @@ -1213,7 +1213,7 @@ function d() { } new Thing(/* dangling */); Thing(/* dangling */); -export /* dangling */{}; +export /* dangling */ {}; ================================================================================ `; @@ -1476,9 +1476,9 @@ export { =====================================output===================================== export //comment -{} + {} -export /* comment */{} +export /* comment */ {} const foo = "" export { @@ -1532,9 +1532,9 @@ export { =====================================output===================================== export //comment -{}; + {}; -export /* comment */{}; +export /* comment */ {}; const foo = ""; export {