Skip to content

Commit

Permalink
feat: allow multiple comments atop of script/style tags
Browse files Browse the repository at this point in the history
  • Loading branch information
dummdidumm committed Mar 22, 2023
1 parent 4ce3dd3 commit bc9e741
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- (feat) support `requirePragma` and `insertPragma` options ([#350](https://github.com/sveltejs/prettier-plugin-svelte/issues/350))
- (feat) support `<svelte:document>`
- (feat) trim whitespace in `class` attributes ([#339](https://github.com/sveltejs/prettier-plugin-svelte/issues/339))
- (feat) allow multiple comments atop of script/style tags ([#291](https://github.com/sveltejs/prettier-plugin-svelte/issues/291))
- (fix) handle script/style attributes without quotes ([#344](https://github.com/sveltejs/prettier-plugin-svelte/issues/344))

## 2.9.0
Expand Down
30 changes: 20 additions & 10 deletions src/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
isTypeScript,
printRaw,
} from './print/node-helpers';
import { ElementNode, Node, ScriptNode, StyleNode } from './print/nodes';
import { CommentNode, ElementNode, Node, ScriptNode, StyleNode } from './print/nodes';

const {
builders: { concat, hardline, softline, indent, dedent, literalline },
Expand Down Expand Up @@ -193,11 +193,16 @@ function embedTag(
const node: ScriptNode | StyleNode | ElementNode = path.getNode();
const content =
tag === 'template' ? printRaw(node as ElementNode, text) : getSnippedContent(node);
const previousComment = getLeadingComment(path);
const previousComments =
node.type === 'Script' || node.type === 'Style'
? node.comments
: [getLeadingComment(path)]
.filter(Boolean)
.map((comment) => ({ comment: comment as CommentNode, emptyLineAfter: false }));

const canFormat =
isNodeSupportedLanguage(node) &&
!isIgnoreDirective(previousComment) &&
!isIgnoreDirective(previousComments[previousComments.length - 1]?.comment) &&
(tag !== 'template' ||
options.plugins.some(
(plugin) => typeof plugin !== 'string' && plugin.parsers && plugin.parsers.pug,
Expand All @@ -223,16 +228,21 @@ function embedTag(
]);
let result = groupConcat([openingTag, body, '</', tag, '>']);

const comments = [];
for (const comment of previousComments) {
comments.push('<!--', comment.comment.data, '-->');
comments.push(hardline);
if (comment.emptyLineAfter) {
comments.push(hardline);
}
}

if (isTopLevel && options.svelteSortOrder !== 'none') {
// top level embedded nodes have been moved from their normal position in the
// node tree. if there is a comment referring to it, it must be recreated at
// the new position.
if (previousComment) {
result = concat(['<!--', previousComment.data, '-->', hardline, result, hardline]);
} else {
result = concat([result, hardline]);
}
return concat([...comments, result, hardline]);
} else {
return comments.length ? concat([...comments, result]) : result;
}

return result;
}
66 changes: 66 additions & 0 deletions src/print/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
import {
ASTNode,
AttributeNode,
CommentInfo,
CommentNode,
IfBlockNode,
Node,
Expand Down Expand Up @@ -100,6 +101,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
}

if (isASTNode(n)) {
assignCommentsToNodes(n);
return printTopLevelParts(n, options, path, print);
}

Expand Down Expand Up @@ -786,6 +788,70 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
throw new Error('unknown node type: ' + node.type);
}

function assignCommentsToNodes(ast: ASTNode) {
if (ast.module) {
ast.module.comments = removeAndGetLeadingComments(ast, ast.module);
}
if (ast.instance) {
ast.instance.comments = removeAndGetLeadingComments(ast, ast.instance);
}
if (ast.css) {
ast.css.comments = removeAndGetLeadingComments(ast, ast.css);
}
}

/**
* Returns the comments that are above the current node and deletes them from the html ast.
*/
function removeAndGetLeadingComments(ast: ASTNode, current: Node): CommentInfo[] {
const siblings = getChildren(ast.html);
const comments: CommentNode[] = [];
const newlines: TextNode[] = [];

if (!siblings.length) {
return [];
}

let node: Node = current;
let prev: Node | undefined = siblings.find((child) => child.end === node.start);
while (prev) {
if (
prev.type === 'Comment' &&
!isIgnoreStartDirective(prev) &&
!isIgnoreEndDirective(prev)
) {
comments.push(prev);
if (comments.length !== newlines.length) {
newlines.push({ type: 'Text', data: '', raw: '', start: -1, end: -1 });
}
} else if (isEmptyTextNode(prev)) {
newlines.push(prev);
} else {
break;
}

node = prev;
prev = siblings.find((child) => child.end === node.start);
}

newlines.length = comments.length; // could be one more if first comment is preceeded by empty text node

for (const comment of comments) {
siblings.splice(siblings.indexOf(comment), 1);
}

for (const text of newlines) {
siblings.splice(siblings.indexOf(text), 1);
}

return comments
.map((comment, i) => ({
comment,
emptyLineAfter: getUnencodedText(newlines[i]).split('\n').length > 2,
}))
.reverse();
}

function printTopLevelParts(
n: ASTNode,
options: ParserOptions,
Expand Down
16 changes: 9 additions & 7 deletions src/print/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,17 +207,20 @@ export interface StyleNode extends BaseNode {
attributes: Node[];
children: Node[];
content: StyleProgramNode;
comments: CommentInfo[]; // doesn't exist on original node but we use it to store comments
}

export interface ScriptNode extends BaseNode {
type: 'Script';
attributes: Node[];
content: Node;
comments: CommentInfo[]; // doesn't exist on original node but we use it to store comments
}

export interface StyleProgramNode extends BaseNode {
type: 'StyleProgram';
styles: string;
comments: CommentInfo[]; // doesn't exist on original node but we use it to store comments
}

export interface ProgramNode extends BaseNode {
Expand Down Expand Up @@ -283,6 +286,11 @@ export interface ConstTagNode extends BaseNode {
expression: Node;
}

export interface CommentInfo {
comment: CommentNode;
emptyLineAfter: boolean;
}

export type Node =
| FragmentNode
| ElementNode
Expand Down Expand Up @@ -334,13 +342,7 @@ export type Node =
*/
export interface ASTNode {
html: Node;
css?: Node & {
attributes: Node[];
children: Node[];
content: Node & {
styles: string;
};
};
css?: StyleNode;
js?: ScriptNode;
instance?: ScriptNode;
module?: ScriptNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
</div>

<!-- i stay above style -->

<style>
.a {
color: blue;
Expand Down
2 changes: 1 addition & 1 deletion test/printer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ for (const file of files) {
const ending = file.split('.').pop();
const input = readFileSync(`test/printer/samples/${file}`, 'utf-8').replace(/\r?\n/g, '\n');
const options = readOptions(
`test/printer/samples/${file.replace(`.${ending}`, '.options.json')}`,
`test/printer/samples/${file.replace('.only', '').replace(`.${ending}`, '.options.json')}`,
);

test(`printer: ${file.slice(0, file.length - `.${ending}`.length)}`, (t) => {
Expand Down
11 changes: 11 additions & 0 deletions test/printer/samples/comments-multiple-above-blocks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- asd1 -->
<!-- asd2 -->
<script></script>

<!-- asd3 -->
<!-- asd4 -->
<div />

<!-- asd5 -->
<!-- asd6 -->
<style></style>
2 changes: 2 additions & 0 deletions test/printer/samples/sort-order-none2.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ <h1>Hello {name}!</h1>

<svelte:options />

<!-- hi -->
<p>yeah why not</p>

<!-- hi -->
<script context="module">
const name1 = "world";
Expand Down

0 comments on commit bc9e741

Please sign in to comment.