Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
03cbbb8
Add sass-parser as a new language
jgerigmeyer Jun 26, 2025
3fe63ea
delete some unused files
jgerigmeyer Jun 26, 2025
5df96c9
Add first sassparser test
jgerigmeyer Jul 3, 2025
8cdef43
Add more tests
jgerigmeyer Jul 17, 2025
91dd0ba
support @each
jgerigmeyer Jul 17, 2025
c417b35
@for
jgerigmeyer Jul 17, 2025
4127817
One step closer
jgerigmeyer Jul 25, 2025
518b07b
Two steps forward and one step back?
jgerigmeyer Jul 31, 2025
28d7734
Merge branch 'main' into sass-parser-2
jgerigmeyer Aug 12, 2025
6685363
upgrade sass-parser
jgerigmeyer Aug 12, 2025
0099ad1
Add support for `@while`
jgerigmeyer Aug 14, 2025
96596b0
Add @use formatting
jgerigmeyer Aug 21, 2025
20676f8
Add @warn and @error
jgerigmeyer Aug 21, 2025
fcaeaec
add @debug
jgerigmeyer Aug 21, 2025
4434ace
Add support for `@forward` with members
jgerigmeyer Aug 22, 2025
69933f7
Add more tests...
jgerigmeyer Aug 22, 2025
e28e09e
Preserve @each formatting
jgerigmeyer Aug 22, 2025
5f6a657
more tests and fixes
jgerigmeyer Sep 11, 2025
3c5ff6d
add map tests
jgerigmeyer Sep 15, 2025
2cc1f62
add interpolation tests
jgerigmeyer Sep 16, 2025
15a505c
add math tests
jgerigmeyer Sep 16, 2025
d131634
Merge branch 'main' into sass-parser-2
jgerigmeyer Sep 17, 2025
fa97670
add include tests
jgerigmeyer Sep 17, 2025
c6f13c9
merge in changes
jgerigmeyer Sep 17, 2025
67c7c4b
add (failing) comment tests
jgerigmeyer Sep 17, 2025
f565d6f
add no-semicolon tests
jgerigmeyer Sep 17, 2025
f5299fa
add parens tests
jgerigmeyer Sep 17, 2025
7e4daf0
add remaining tests
jgerigmeyer Sep 17, 2025
82382e9
upgrade sass-parser
jgerigmeyer Sep 17, 2025
e02766b
replace scss tests with sassparser
jgerigmeyer Sep 18, 2025
f194622
minimize test diff
jgerigmeyer Sep 18, 2025
8659de7
Merge branch 'main' into sass-parser-2
jgerigmeyer Sep 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"remark-footnotes": "2.0.0",
"remark-math": "3.0.1",
"remark-parse": "8.0.3",
"sass-parser": "0.4.28",
"sdbm": "3.0.0",
"search-closest": "1.1.0",
"smol-toml": "1.4.2",
Expand Down
18 changes: 18 additions & 0 deletions src/language-sassparser/embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { hardline } from "../document/builders.js";
import printFrontMatter from "../utils/front-matter/print.js";

function embed(path) {
const { node } = path;

if (node.type === "front-matter") {
return async (textToDoc) => {
const doc = await printFrontMatter(node, textToDoc);
return doc ? [doc, hardline] : undefined;
};
}
}

// `front-matter` only available on `root`
embed.getVisitorKeys = (node) => (node.type === "root" ? ["frontMatter"] : []);

export default embed;
6 changes: 6 additions & 0 deletions src/language-sassparser/get-visitor-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import createGetVisitorKeys from "../utils/create-get-visitor-keys.js";
import visitorKeys from "./visitor-keys.js";

const getVisitorKeys = createGetVisitorKeys(visitorKeys, "sassType");

export default getVisitorKeys;
8 changes: 8 additions & 0 deletions src/language-sassparser/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import printer from "./printer-postcss.js";

export const printers = {
postcss: printer,
};
export { default as languages } from "./languages.evaluate.js";
export { default as options } from "./options.js";
export * as parsers from "./parser-postcss.js";
12 changes: 12 additions & 0 deletions src/language-sassparser/languages.evaluate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as linguistLanguages from "linguist-languages";
import createLanguage from "../utils/create-language.js";

const languages = [
createLanguage(linguistLanguages.SCSS, (data) => ({
parsers: ["sassparser"],
vscodeLanguageIds: ["scss"],
extensions: [...data.extensions, ".sassparser"],
})),
];

export default languages;
77 changes: 77 additions & 0 deletions src/language-sassparser/loc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import isNonEmptyArray from "../utils/is-non-empty-array.js";
import lineColumnToIndex from "../utils/line-column-to-index.js";
import { skipEverythingButNewLine } from "../utils/skip.js";

function calculateLocStart(node, text) {
// `postcss>=8`
if (typeof node.source?.start?.offset === "number") {
return node.source.start.offset;
}

if (node.source?.start) {
return lineColumnToIndex(node.source.start, text);
}

/* c8 ignore next */
throw Object.assign(new Error("Can not locate node."), { node });
}

function calculateLocEnd(node, text) {
if (node.sassType === "sass-comment") {
return skipEverythingButNewLine(text, node.source.startOffset);
}

// `postcss>=8`
if (typeof node.source?.end?.offset === "number") {
return node.source.end.offset;
}

if (node.source) {
if (node.source.end) {
const index = lineColumnToIndex(node.source.end, text);
return index;
}

if (isNonEmptyArray(node.nodes)) {
return calculateLocEnd(node.nodes.at(-1), text);
}
}

return null;
}

function calculateLoc(node, text) {
// TODO: "configuration" nodes do not have `source.span` implemented yet
if (node.sassType === "configuration") {
return;
}

if (node.source) {
node.source.startOffset = calculateLocStart(node, text);
node.source.endOffset = calculateLocEnd(node, text);
}

for (const key in node) {
const child = node[key];

if (
["parent", "source"].includes(key) ||
!child ||
typeof child !== "object"
) {
continue;
}

calculateLoc(child, text);
}
}

function locStart(node) {
return node.source?.startOffset;
}

function locEnd(node) {
return node.source?.endOffset;
}

export { calculateLoc, locEnd, locStart };
8 changes: 8 additions & 0 deletions src/language-sassparser/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import commonOptions from "../common/common-options.evaluate.js";

// format based on https://github.com/prettier/prettier/blob/main/src/main/core-options.evaluate.js
const options = {
singleQuote: commonOptions.singleQuote,
};

export default options;
63 changes: 63 additions & 0 deletions src/language-sassparser/parser-postcss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { scss as postcssScssParse } from "sass-parser";
import createError from "../common/parser-create-error.js";
import parseFrontMatter from "../utils/front-matter/parse.js";
import { calculateLoc, locEnd, locStart } from "./loc.js";
import { hasIgnorePragma, hasPragma } from "./pragma.js";

function parseWithParser(parse, text, options) {
const parsed = parseFrontMatter(text);
const { frontMatter } = parsed;
text = parsed.content;

let result;

try {
result = parse(text, {
// Prevent file access https://github.com/postcss/postcss/blob/4f4e2932fc97e2c117e1a4b15f0272ed551ed59d/lib/previous-map.js#L18
map: false,
});
} catch (/** @type {any} */ error) {
const { name, reason, line, column } = error;
/* c8 ignore 3 */
if (typeof line !== "number") {
throw error;
}

throw createError(`${name}: ${reason}`, {
loc: { start: { line, column } },
cause: error,
});
}

options.originalText = text;

calculateLoc(result, text);

if (frontMatter) {
frontMatter.source = {
startOffset: 0,
endOffset: frontMatter.raw.length,
};
result.frontMatter = frontMatter;
}

return result;
}

function parseScss(text, options = {}) {
return parseWithParser(
(text, opts) => postcssScssParse.parse(text, opts),
text,
options,
);
}

const postCssParser = {
astFormat: "postcss",
hasPragma,
hasIgnorePragma,
locStart,
locEnd,
};

export const sassparser = { ...postCssParser, parse: parseScss };
23 changes: 23 additions & 0 deletions src/language-sassparser/pragma.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
hasIgnorePragma as jsHasIgnorePragma,
hasPragma as jsHasPragma,
insertPragma as jsInsertPragma,
} from "../language-js/pragma.js";
import parseFrontMatter from "../utils/front-matter/parse.js";

function hasPragma(text) {
return jsHasPragma(parseFrontMatter(text).content);
}

function hasIgnorePragma(text) {
return jsHasIgnorePragma(parseFrontMatter(text).content);
}

function insertPragma(text) {
const { frontMatter, content } = parseFrontMatter(text);
return (
(frontMatter ? frontMatter.raw + "\n\n" : "") + jsInsertPragma(content)
);
}

export { hasIgnorePragma, hasPragma, insertPragma };
5 changes: 5 additions & 0 deletions src/language-sassparser/print/css-units.evaluate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import cssUnits from "css-units-list";

const CSS_UNITS = new Map(cssUnits.map((unit) => [unit.toLowerCase(), unit]));

export default CSS_UNITS;
45 changes: 45 additions & 0 deletions src/language-sassparser/print/misc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ifBreak } from "../../document/builders.js";
import printNumber from "../../utils/print-number.js";
import { hasComma, isVarFunctionNode } from "../utils/index.js";
import CSS_UNITS from "./css-units.evaluate.js";

function printUnit(unit) {
const lowercased = unit.toLowerCase();
return CSS_UNITS.has(lowercased) ? CSS_UNITS.get(lowercased) : unit;
}

function printCssNumber(rawNumber) {
return (
printNumber(rawNumber)
// Remove trailing `.0`.
.replace(/\.0(?=$|e)/u, "")
);
}

function shouldPrintTrailingComma(options) {
return options.trailingComma === "es5" || options.trailingComma === "all";
}

function printTrailingComma(path, options) {
if (isVarFunctionNode(path.grandparent) && hasComma(path, options)) {
return ",";
}
if (
path.node.type !== "comment" &&
shouldPrintTrailingComma(options) &&
path.callParent(
() => path.node.sassType === "map" || path.node.sassType === "list",
)
) {
return ifBreak(",");
}

return "";
}

export {
printCssNumber,
printTrailingComma,
printUnit,
shouldPrintTrailingComma,
};
50 changes: 50 additions & 0 deletions src/language-sassparser/print/sequence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { hardline, line } from "../../document/builders.js";
import isFrontMatter from "../../utils/front-matter/is-front-matter.js";
import hasNewline from "../../utils/has-newline.js";
import isNextLineEmpty from "../../utils/is-next-line-empty.js";
import { locEnd, locStart } from "../loc.js";

function printSequence(path, options, print) {
const parts = [];
path.each(() => {
const { node, previous } = path;
if (
previous?.type === "comment" &&
previous.text.trim() === "prettier-ignore"
) {
parts.push(options.originalText.slice(locStart(node), locEnd(node)));
} else {
parts.push(print());
}

if (path.isLast) {
return;
}

const { next } = path;
if (
(next.type === "comment" &&
!hasNewline(options.originalText, locStart(next), {
backwards: true,
}) &&
!isFrontMatter(node)) ||
(next.type === "atrule" &&
next.name === "else" &&
node.type !== "comment")
) {
parts.push(" ");
} else {
parts.push(options.__isHTMLStyleAttribute ? line : hardline);
if (
isNextLineEmpty(options.originalText, locEnd(node)) &&
!isFrontMatter(node)
) {
parts.push(hardline);
}
}
}, "nodes");

return parts;
}

export default printSequence;
Loading