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

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Could not find a declaration file for module './utils/frontmatter/parse'. '/home/runner/work/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Could not find a declaration file for module './utils/frontmatter/parse'. '/home/runner/work/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Could not find a declaration file for module './utils/frontmatter/parse'. '/home/runner/work/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Could not find a declaration file for module './utils/frontmatter/parse'. '/Users/runner/work/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Could not find a declaration file for module './utils/frontmatter/parse'. '/Users/runner/work/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Could not find a declaration file for module './utils/frontmatter/parse'. 'D:/a/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

Check failure on line 18 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Could not find a declaration file for module './utils/frontmatter/parse'. 'D:/a/plugin-pug/plugin-pug/src/utils/frontmatter/parse.js' implicitly has an 'any' type.

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

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Cannot find name 'Frontmatter'.

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Cannot find name 'Frontmatter'.

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Cannot find name 'Frontmatter'.

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Cannot find name 'Frontmatter'.

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Cannot find name 'Frontmatter'.

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Cannot find name 'Frontmatter'.

Check failure on line 24 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Cannot find name 'Frontmatter'.
}

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

const parts = parse(text);

Check failure on line 48 in src/index.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Expected parts to have a type annotation
const frontmatter = parts.frontMatter;

Check failure on line 49 in src/index.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Unsafe member access .frontMatter on an `error` typed value

Check failure on line 49 in src/index.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Expected frontmatter to have a type annotation
text = parts.content;

Check failure on line 50 in src/index.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Unsafe member access .content on an `error` typed value

let trimmedAndAlignedContent: string = text.replace(/^\s*\n/, '');
const contentIndentation: RegExpExecArray | null = /^\s*/.exec(
trimmedAndAlignedContent,
Expand All @@ -64,7 +70,7 @@
// 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 @@ -93,13 +99,21 @@
},
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;
print(
path: AstPath,

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Cannot find name 'AstPath'.

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Cannot find name 'AstPath'.

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Cannot find name 'AstPath'.

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Cannot find name 'AstPath'.

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Cannot find name 'AstPath'.

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Cannot find name 'AstPath'.

Check failure on line 103 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Cannot find name 'AstPath'.
options: ParserOptions & PugParserOptions,
print: (path: AstPath) => Doc,

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Cannot find name 'AstPath'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Cannot find name 'AstPath'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Cannot find name 'AstPath'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Cannot find name 'AstPath'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Cannot find name 'AstPath'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Cannot find name 'AstPath'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Cannot find name 'Doc'.

Check failure on line 105 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Cannot find name 'AstPath'.
): Doc {

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Cannot find name 'Doc'.

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Cannot find name 'Doc'.

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Cannot find name 'Doc'.

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Cannot find name 'Doc'.

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Cannot find name 'Doc'.

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Cannot find name 'Doc'.

Check failure on line 106 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Cannot find name 'Doc'.
const entry: AstPathStackEntry = path.stack[0];

Check failure on line 107 in src/index.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Unsafe member access .stack on an `error` typed value
const { content, tokens, frontmatter } = entry;
const pugOptions: PugPrinterOptions = convergeOptions(options);
const printer: PugPrinter = new PugPrinter(content, tokens, pugOptions);
const result: string = await printer.build();
const printer: PugPrinter = new PugPrinter(
content,
tokens,
pugOptions,
frontmatter,
);
const result: string = printer.build();

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

Type 'Promise<string>' is not assignable to type 'string'.

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

Type 'Promise<string>' is not assignable to type 'string'.

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / coverage-pr

Type 'Promise<string>' is not assignable to type 'string'.

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, macos-latest

Type 'Promise<string>' is not assignable to type 'string'.

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, macos-latest

Type 'Promise<string>' is not assignable to type 'string'.

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, windows-latest

Type 'Promise<string>' is not assignable to type 'string'.

Check failure on line 116 in src/index.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, windows-latest

Type 'Promise<string>' is not assignable to type 'string'.
logger.debug('[printers:pug-ast:print]:', result);
return result;
},
Expand Down
17 changes: 15 additions & 2 deletions src/printer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { types } from 'node:util';
import type { BuiltInParserName, Options, RequiredOptions } from 'prettier';
import { format } from 'prettier';
import type PrettierAngularPlugin from 'prettier/plugins/angular';
Expand Down Expand Up @@ -231,7 +230,7 @@
private readonly classLiteralToAttribute: string[] = [];
private readonly classLiteralAfterAttributes: string[] = [];

/**

Check failure on line 233 in src/printer.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Missing JSDoc @param "frontmatter" declaration
* Constructs a new pug printer.
*
* @param content The pug content string.
Expand All @@ -242,7 +241,9 @@
private readonly content: string,
private tokens: Token[],
private readonly options: PugPrinterOptions,
private frontmatter: Frontmatter<string>,

Check failure on line 244 in src/printer.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Member 'frontmatter: Frontmatter<string>' is never reassigned; mark it as `readonly`
) {
this.frontmatter = frontmatter;
this.indentString = options.pugUseTabs
? '\t'
: ' '.repeat(options.pugTabWidth);
Expand Down Expand Up @@ -390,7 +391,10 @@
token = this.getNextToken();
}

return results.join('');
const fm = await this.frontmatterFormat(this.frontmatter.value);

Check failure on line 394 in src/printer.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Unsafe member access .value on an `error` typed value

Check failure on line 394 in src/printer.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Expected fm to have a type annotation
const formattedFrontmatter = this.frontmatter ? '---\n' + fm + '---\n' : '';

Check failure on line 395 in src/printer.ts

View workflow job for this annotation

GitHub Actions / Lint: node-22, ubuntu-latest

Expected formattedFrontmatter to have a type annotation

return formattedFrontmatter + results.join('');
}

private getNextToken(): Token | null {
Expand Down Expand Up @@ -478,6 +482,15 @@
}
}

private async frontmatterFormat(frontmatter: string): Promise<string> {
const options = {
parser: 'yaml',
collectionStyle: 'block',
Copy link
Copy Markdown
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.

};

return format(frontmatter, options);
}

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 = [];
47 changes: 47 additions & 0 deletions src/utils/frontmatter/embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { doc, util } from 'prettier';
// import inferParser from '../../utilities/infer-parser.js';
import isFrontMatter from './is-front-matter.js';

const SUPPORTED_EMBED_LANGUAGES = new Set(['yaml', 'toml']);

const hardline = doc.builders.hardline;
const markAsRoot = doc.builders.markAsRoot;
const inferParser = util.inferParser;

const isEmbedFrontMatter = ({ node } /* , options */) =>
isFrontMatter(node) && SUPPORTED_EMBED_LANGUAGES.has(node.language);

async function printEmbedFrontMatter(textToDoc, print, path, options) {
const { node } = path;
const { language } = node;

if (!SUPPORTED_EMBED_LANGUAGES.has(language)) {
return;
}

const value = node.value.trim();

let doc;
if (value) {
const parser =
language === 'yaml' ? language : inferParser(options, { language });
if (!parser) {
return;
}

doc = value ? await textToDoc(value, { parser }) : '';
} else {
doc = value;
}

return markAsRoot([
node.startDelimiter,
node.explicitLanguage ?? '',
hardline,
doc,
doc ? hardline : '',
node.endDelimiter,
]);
}

export { isEmbedFrontMatter, printEmbedFrontMatter };
7 changes: 7 additions & 0 deletions src/utils/frontmatter/is-front-matter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FRONT_MATTER_MARK } from './constants.js';

function isFrontMatter(node) {
return Boolean(node?.[FRONT_MATTER_MARK]);
}

export default isFrontMatter;
121 changes: 121 additions & 0 deletions src/utils/frontmatter/parse.js
Copy link
Copy Markdown
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
Copy Markdown
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;
5 changes: 5 additions & 0 deletions src/utils/frontmatter/print.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function printFrontMatter({ node }) {
return node.raw;
}

export default printFrontMatter;
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
Copy Markdown
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' )
Loading