Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 22 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import { options as pugOptions } from './options';
import { convergeOptions } from './options/converge';
import type { PugPrinterOptions } from './printer';
import { PugPrinter } from './printer';
import parseFrontmatter from './utils/frontmatter/parse';

/** Ast path stack entry. */
interface AstPathStackEntry {
content: string;
tokens: Token[];
frontmatter: string;
}

/** The plugin object that is picked up by prettier. */
Expand All @@ -43,6 +45,12 @@ export const plugin: Plugin<AstPathStackEntry> = {
parse(text, options) {
logger.debug('[parsers:pug:parse]:', { text });

const parts: FrontMatter = parseFrontmatter(text);
const frontmatter: string = parts.frontMatter
? parts.frontMatter.value
: undefined;
text = parts.content;

let trimmedAndAlignedContent: string = text.replace(/^\s*\n/, '');
const contentIndentation: RegExpExecArray | null = /^\s*/.exec(
trimmedAndAlignedContent,
Expand All @@ -64,7 +72,7 @@ export const plugin: Plugin<AstPathStackEntry> = {
// logger.debug('[parsers:pug:parse]: tokens', JSON.stringify(tokens, undefined, 2));
// const ast: AST = parse(tokens, {});
// logger.debug('[parsers:pug:parse]: ast', JSON.stringify(ast, undefined, 2));
return { content, tokens };
return { content, tokens, frontmatter };
},

astFormat: 'pug-ast',
Expand Down Expand Up @@ -94,11 +102,20 @@ export const plugin: Plugin<AstPathStackEntry> = {
printers: {
'pug-ast': {
// @ts-expect-error: Prettier allow it to be async if we don't do recursively print
async print(path, options: ParserOptions & PugParserOptions) {
const entry: AstPathStackEntry = path.stack[0]!;
const { content, tokens } = entry;
async print(
path: AstPath,
options: ParserOptions & PugParserOptions,
print: (path: AstPath) => Doc,
): Doc {
const entry: AstPathStackEntry = path.stack[0];
const { content, tokens, frontmatter } = entry;
const pugOptions: PugPrinterOptions = convergeOptions(options);
const printer: PugPrinter = new PugPrinter(content, tokens, pugOptions);
const printer: PugPrinter = new PugPrinter(
content,
tokens,
pugOptions,
frontmatter,
);
const result: string = await printer.build();
logger.debug('[printers:pug-ast:print]:', result);
return result;
Expand Down
26 changes: 26 additions & 0 deletions src/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,15 @@ export class PugPrinter {
* @param content The pug content string.
* @param tokens The pug token array.
* @param options Options for the printer.
* @param frontmatter The extracted frontmatter part.
*/
public constructor(
private readonly content: string,
private tokens: Token[],
private readonly options: PugPrinterOptions,
private readonly frontmatter: string,
) {
this.frontmatter = frontmatter;
this.indentString = options.pugUseTabs
? '\t'
: ' '.repeat(options.pugTabWidth);
Expand Down Expand Up @@ -390,6 +393,18 @@ export class PugPrinter {
token = this.getNextToken();
}

if (this.frontmatter) {
try {
results.unshift(
'---\n',
await this.frontmatterFormat(this.frontmatter),
'---\n',
);
} catch (error: any) {
logger.warn(error);
}
}

return results.join('');
}

Expand Down Expand Up @@ -478,6 +493,17 @@ export class PugPrinter {
}
}

private async frontmatterFormat(frontmatter: string): Promise<string> {
const options: Options = {
parser: 'yaml',
collectionStyle: 'block',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should enforce formatting of flow style array and dict but it doesn't and I don't know why.

};

const result: string = await format(frontmatter, options);

return result;
}

private async frameworkFormat(code: string): Promise<string> {
const options: Options = {
...this.codeInterpolationOptions,
Expand Down
3 changes: 3 additions & 0 deletions src/utils/frontmatter/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/** @type {unique symbol} */
export const FRONT_MATTER_MARK = Symbol.for("PRETTIER_IS_FRONT_MATTER");
export const FRONT_MATTER_VISITOR_KEYS = [];
121 changes: 121 additions & 0 deletions src/utils/frontmatter/parse.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (blocking): CLI is telling that these files are not TypeScript files, but JavaScript. You need to use TypeScript.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will be too much for me right now. Thanks for the support.

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import replaceNonLineBreaksWithSpace from './replace-non-line-breaks-with-space.js';
import { FRONT_MATTER_MARK } from './constants.js';

const DELIMITER_LENGTH = 3;

/**
@typedef {{
index: number,
// 1-based line number
line: number,
// 0-based column number
column: number,
}} Position
@typedef {{
language: string,
explicitLanguage: string | null,
value: string,
startDelimiter: string,
endDelimiter: string,
raw: string,
start: Position,
end: Position,
[FRONT_MATTER_MARK]: true,
}} FrontMatter
*/

/**
@param {string} text
@returns {FrontMatter | undefined}
*/
function getFrontMatter(text) {
const startDelimiter = text.slice(0, DELIMITER_LENGTH);

if (startDelimiter !== '---' && startDelimiter !== '+++') {
return;
}

const firstLineBreakIndex = text.indexOf('\n', DELIMITER_LENGTH);
if (firstLineBreakIndex === -1) {
return;
}

const explicitLanguage = text
.slice(DELIMITER_LENGTH, firstLineBreakIndex)
.trim();

let endDelimiterIndex = text.indexOf(
`\n${startDelimiter}`,
firstLineBreakIndex,
);

let language = explicitLanguage;
if (!language) {
language = startDelimiter === '+++' ? 'toml' : 'yaml';
}

if (
endDelimiterIndex === -1 &&
startDelimiter === '---' &&
language === 'yaml'
) {
// In some markdown processors such as pandoc,
// "..." can be used as the end delimiter for YAML front-matter.
endDelimiterIndex = text.indexOf('\n...', firstLineBreakIndex);
}

if (endDelimiterIndex === -1) {
return;
}

const frontMatterEndIndex = endDelimiterIndex + 1 + DELIMITER_LENGTH;

const nextCharacter = text.charAt(frontMatterEndIndex + 1);
if (!/\s?/.test(nextCharacter)) {
return;
}

const raw = text.slice(0, frontMatterEndIndex);
/** @type {string[]} */
let lines;

return {
language,
explicitLanguage: explicitLanguage || null,
value: text.slice(firstLineBreakIndex + 1, endDelimiterIndex),
startDelimiter,
endDelimiter: raw.slice(-DELIMITER_LENGTH),
raw,
start: { line: 1, column: 0, index: 0 },
end: {
index: raw.length,
get line() {
lines ??= raw.split('\n');
return lines.length;
},
get column() {
lines ??= raw.split('\n');
return lines.at(-1).length;
},
},
[FRONT_MATTER_MARK]: true,
};
}

function parse(text) {
const frontMatter = getFrontMatter(text);

if (!frontMatter) {
return { content: text };
}

return {
frontMatter,
get content() {
const { raw } = frontMatter;
return replaceNonLineBreaksWithSpace(raw) + text.slice(raw.length);
},
};
}

export default parse;
19 changes: 19 additions & 0 deletions src/utils/frontmatter/replace-non-line-breaks-with-space.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// import * as assert from "#universal/assert";
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea how to put it back


/**
Replaces all characters in the input string except line breaks `\n` with a space.
@param {string} string - The input string to process.
@returns {string}
*/
function replaceNonLineBreaksWithSpace(string) {
const replaced = string.replaceAll(/[^\n]/g, ' ');

if (process.env.NODE_ENV !== 'production') {
// assert.equal(replaced.length, string.length);
}

return replaced;
}

export default replaceNonLineBreaksWithSpace;
33 changes: 33 additions & 0 deletions tests/frontmatter/formatted.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: YAML test
tags:
- this
- squence
- should
- be
- indented
lets:
have:
us:
some: nesting
and: some more
flow style not supported:
key: value
bar: baz
not even arrays:
- 1
- 2
- 3
- 4
- 5
---
doctype
html
head
title This is a !{ name }!

body
h1 Hello world!
h2= name

#example
9 changes: 9 additions & 0 deletions tests/frontmatter/frontmatter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { compareFiles } from 'tests/common';
import { describe, expect, it } from 'vitest';

describe('Frontmatter', () => {
it('should format yaml frontmatter', async () => {
const { expected, actual } = await compareFiles(import.meta.url);
expect(actual).toBe(expected);
});
});
27 changes: 27 additions & 0 deletions tests/frontmatter/unformatted.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: YAML test
tags:
- this
- squence
- should
- be
- indented
lets:
have:
us:
some: nesting
and: some more
flow style not supported:
{key: value, bar: baz}
not even arrays: [1,2,3,4,5]
---
doctype
html
head
title This is a !{name}!

body
h1 Hello world!
h2= name

div( id='example' )