Skip to content

Commit

Permalink
Add support for .sass formatting (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
drwpow authored Oct 7, 2021
1 parent 915a6e2 commit 20a298e
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 150 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-clocks-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'prettier-plugin-astro': minor
---

Add support for .sass formatting
22 changes: 16 additions & 6 deletions src/printer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const {
builders: { join, fill, line, literalline, hardline, softline, group, breakParent, indent, dedent },
builders: { breakParent, dedent, fill, group, hardline, indent, join, line, literalline, softline },
utils: { removeLines },
} = require('prettier/doc');
const { SassFormatter } = require('sass-formatter');
Expand All @@ -8,12 +8,14 @@ const { parseSortOrder } = require('./options');
const {
attachCommentsHTML,
canOmitSoftlineBeforeClosingTag,
dedent: manualDedent,
endsWithLinebreak,
flatten,
forceIntoExpression,
formattableAttributes,
getText,
getUnencodedText,
indent: manualIndent,
isASTNode,
isEmptyDoc,
isEmptyTextNode,
Expand Down Expand Up @@ -446,12 +448,12 @@ function embed(path, print, textToDoc, opts) {
case 'scss': {
// the css parser appends an extra indented hardline, which we want outside of the `indent()`,
// so we remove the last element of the array
const [formatttedStyles, ,] = textToDoc(node.content.styles, { ...opts, parser: parserLang });
const [formattedStyles, ,] = textToDoc(node.content.styles, { ...opts, parser: parserLang });

const attributes = path.map((childPath) => childPath.call(print), 'attributes');
const styleGroup = group(['<style', indent(group(attributes)), softline, '>']);

return group([styleGroup, indent([hardline, formatttedStyles]), hardline, '</style>', hardline]);
return group([styleGroup, indent([hardline, formattedStyles]), hardline, '</style>', hardline]);
}
case 'sass': {
const sassOptions = {
Expand All @@ -460,11 +462,19 @@ function embed(path, print, textToDoc, opts) {
lineEnding: opts.endOfLine.toUpperCase(),
};

let formatttedSass = SassFormatter.Format(node.content.styles, sassOptions).trim().split('\n');
formatttedSass = join(hardline, formatttedSass);
// dedent the .sass, otherwise SassFormatter gets indentation wrong
const { result: raw, tabSize } = manualDedent(node.content.styles);

// format + re-indent
let formattedSass = SassFormatter.Format(raw, sassOptions).trim();
const indentChar = new Array(Math.max(tabSize, 2) + 1).join(opts.useTabs ? '\t' : ' ');
formattedSass = manualIndent(formattedSass, indentChar);

// print
formattedSass = join(hardline, formattedSass.split('\n'));
const attributes = path.map((childPath) => childPath.call(print), 'attributes');
const styleGroup = group(['<style', indent(group(attributes)), softline, '>']);
return group([styleGroup, hardline, formatttedSass, hardline, '</style>', hardline]);
return group([styleGroup, hardline, formattedSass, hardline, '</style>', hardline]);
}
}
}
Expand Down
42 changes: 42 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -555,15 +555,57 @@ function attachCommentsHTML(node) {
});
}

/* dedent string & return tabSize (the last part is what we need) */
function dedent(input) {
let minTabSize = Infinity;
let result = input;
// 1. normalize
result = result.replace(/\r\n/g, '\n');

// 2. count tabSize
let char = '';
for (const line of result.split('\n')) {
if (!line) continue;
// if any line begins with a non-whitespace char, minTabSize is 0
if (line[0] && /^[^\s]/.test(line[0])) {
minTabSize = 0;
break;
}
const match = line.match(/^(\s+)\S+/); // \S ensures we don’t count lines of pure whitespace
if (match) {
if (match[1] && !char) char = match[1][0];
if (match[1].length < minTabSize) minTabSize = match[1].length;
}
}

// 3. reformat string
if (minTabSize > 0 && Number.isFinite(minTabSize)) {
result = result.replace(new RegExp(`^${new Array(minTabSize + 1).join(char)}`, 'gm'), '');
}

return {
tabSize: minTabSize === Infinity ? 0 : minTabSize,
char,
result,
};
}

/* re-indent string by chars */
function indent(input, char = ' ') {
return input.replace(/^(.)/gm, `${char}$1`);
}

module.exports = {
attachCommentsHTML,
canOmitSoftlineBeforeClosingTag,
dedent,
endsWithLinebreak,
flatten,
forceIntoExpression,
formattableAttributes,
getText,
getUnencodedText,
indent,
isASTNode,
isAttributeShorthand,
isBlockElement,
Expand Down
7 changes: 3 additions & 4 deletions test/astro-prettier.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,11 @@ test('can format a basic Astro file with styles', Prettier, 'with-styles');

test(`Can format an Astro file with attributes in the <style> tag`, Prettier, 'style-tag-attributes');

test('can format a basic Astro file with SCSS styles', Prettier, 'with-scss');
test('can format a basic Astro file with .scss styles', Prettier, 'with-scss');

// TODO make this both test "does not fail on SASS & prints the raw source & a test that specifies whether we can actually format the SASS w/ an embed"
test.todo('can return the basic SASS style block & remove extraneous whitespace - extremely naive support');
test('can clean up whitespace within .sass styles (but can’t format them)', Prettier, 'with-sass');

test('can format a basic Astro file with styles written in Indented SASS', Prettier, 'with-indented-sass');
test('can format a basic Astro file with styles written in .sass', Prettier, 'with-indented-sass');

test('can format an Astro file with frontmatter', Prettier, 'frontmatter');

Expand Down
28 changes: 14 additions & 14 deletions test/fixtures/in/with-indented-sass.astro
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@
<style lang="sass">


$bg: #1E1E1E
$text: #9cdcfe
$shadow: #0002
$fonts: monaco, Consolas, 'Lucida Console', monospace

body
padding: 0
margin: 0
background: $bg
color: $text
font-family: $fonts
display: flex
flex-flow: column
height: 100vh
$bg: #1E1E1E
$text: #9cdcfe
$shadow: #0002
$fonts: monaco, Consolas, 'Lucida Console', monospace

body
padding: 0
margin: 0
background: $bg
color: $text
font-family: $fonts
display: flex
flex-flow: column
height: 100vh

.header
background: lighten($bg, 2.5)
Expand Down
47 changes: 47 additions & 0 deletions test/fixtures/in/with-sass.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>
Hello world!</h1>
</body>
</html>



<style lang="sass">
.hello
color: red



p
background-color: white

// hidden comment

nav
ul
margin: 0
padding: 0
list-style: none


/* visible comment */

li
display: inline-block

a
display: block
padding: 6px 12px
text-decoration: none



</style>
4 changes: 4 additions & 0 deletions test/fixtures/in/with-scss.astro
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
background-color: white;
}
}

// hidden comment

nav {
ul {
Expand All @@ -29,6 +31,8 @@
list-style: none;
}

/* visible comment */

li { display: inline-block; }

a {
Expand Down
Loading

0 comments on commit 20a298e

Please sign in to comment.