@prettier/plugin-pug
Advanced tools
Comparing version 1.1.5 to 1.1.6
# Next | ||
[diff](https://github.com/prettier/plugin-pug/compare/1.1.5...master) | ||
[diff](https://github.com/prettier/plugin-pug/compare/1.1.6...master) | ||
# 1.1.6 | ||
[diff](https://github.com/prettier/plugin-pug/compare/1.1.5...1.1.6) | ||
- Improve handling of Angular attribute values ([#67], [#68]) | ||
- Restructure internal code | ||
- Add issue templates | ||
[#67]: https://github.com/prettier/plugin-pug/issues/67 | ||
[#68]: https://github.com/prettier/plugin-pug/issues/68 | ||
# 1.1.5 | ||
@@ -6,0 +17,0 @@ |
@@ -23,3 +23,3 @@ "use strict"; | ||
result += input | ||
.substring(firstNonSpace) | ||
.slice(firstNonSpace) | ||
.trim() | ||
@@ -32,3 +32,3 @@ .replace(/\s\s+/g, ' '); | ||
result = result.replace(/\s\s+/g, ' '); | ||
if (!pipeless && input.startsWith(' ')) { | ||
if (!pipeless && input[0] === ' ') { | ||
result = ` ${result}`; | ||
@@ -84,2 +84,2 @@ } | ||
}; | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3B0aW9ucy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFhLFFBQUEsWUFBWSxHQUFXLEtBQUssQ0FBQztBQVMxQyxTQUFnQiwrQkFBK0IsQ0FBQyxrQkFBMEM7SUFDekYsUUFBUSxrQkFBa0IsRUFBRTtRQUMzQixLQUFLLFFBQVE7WUFDWixPQUFPLElBQUksQ0FBQztRQUNiLEtBQUssV0FBVztZQUNmLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7SUFDRCxNQUFNLElBQUksS0FBSyxDQUNkLHFEQUFxRCxrQkFBa0IsOENBQThDLENBQ3JILENBQUM7QUFDSCxDQUFDO0FBVkQsMEVBVUM7QUFFRCxTQUFnQiwyQkFBMkIsQ0FDMUMsS0FBYSxFQUNiLHFCQUE0QyxFQUM1QyxXQUFvQixLQUFLO0lBRXpCLFFBQVEscUJBQXFCLEVBQUU7UUFDOUIsS0FBSyxjQUFjLENBQUMsQ0FBQztZQUNwQixJQUFJLE1BQU0sR0FBVyxFQUFFLENBQUM7WUFDeEIsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFDO1lBQ3RCLEtBQUssYUFBYSxFQUFFLGFBQWEsR0FBRyxLQUFLLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxhQUFhLENBQUMsS0FBSyxHQUFHLEVBQUUsYUFBYSxFQUFFLEVBQUU7Z0JBQ2xHLE1BQU0sSUFBSSxHQUFHLENBQUM7YUFDZDtZQUNELE1BQU0sSUFBSSxLQUFLO2lCQUNiLFNBQVMsQ0FBQyxhQUFhLENBQUM7aUJBQ3hCLElBQUksRUFBRTtpQkFDTixPQUFPLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3pCLE9BQU8sTUFBTSxDQUFDO1NBQ2Q7UUFDRCxLQUFLLFVBQVUsQ0FBQyxDQUFDO1lBQ2hCLElBQUksTUFBTSxHQUFXLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDdkMsSUFBSSxDQUFDLFFBQVEsSUFBSSxLQUFLLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFO2dCQUN2QyxNQUFNLEdBQUcsSUFBSSxNQUFNLEVBQUUsQ0FBQzthQUN0QjtZQUNELE9BQU8sTUFBTSxDQUFDO1NBQ2Q7UUFDRCxLQUFLLFVBQVUsQ0FBQztRQUNoQjtZQUVDLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7QUFDRixDQUFDO0FBL0JELGtFQStCQztBQUVZLFFBQUEsT0FBTyxHQUFHO0lBQ3RCLGtCQUFrQixFQUFFO1FBQ25CLEtBQUssRUFBRSxPQUFPO1FBQ2QsUUFBUSxFQUFFLG9CQUFZO1FBQ3RCLElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLFFBQVE7UUFDakIsV0FBVyxFQUFFLHlEQUF5RDtRQUN0RSxPQUFPLEVBQUU7WUFDUjtnQkFDQyxLQUFLLEVBQUUsUUFBUTtnQkFDZixXQUFXLEVBQ1Ysc0dBQXNHO2FBQ3ZHO1lBQ0Q7Z0JBQ0MsS0FBSyxFQUFFLFdBQVc7Z0JBQ2xCLFdBQVcsRUFDVixnSEFBZ0g7YUFDakg7U0FDRDtLQUNEO0lBQ0QscUJBQXFCLEVBQUU7UUFDdEIsS0FBSyxFQUFFLE9BQU87UUFDZCxRQUFRLEVBQUUsb0JBQVk7UUFDdEIsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsVUFBVTtRQUNuQixXQUFXLEVBQUUsNENBQTRDO1FBQ3pELE9BQU8sRUFBRTtZQUNSO2dCQUNDLEtBQUssRUFBRSxVQUFVO2dCQUNqQixXQUFXLEVBQUUsMEVBQTBFO2FBQ3ZGO1lBQ0Q7Z0JBQ0MsS0FBSyxFQUFFLGNBQWM7Z0JBQ3JCLFdBQVcsRUFBRSx5RUFBeUU7YUFDdEY7WUFDRDtnQkFDQyxLQUFLLEVBQUUsVUFBVTtnQkFDakIsV0FBVyxFQUFFLGtFQUFrRTthQUMvRTtTQUNEO0tBQ0Q7Q0FDRCxDQUFDIn0= | ||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3B0aW9ucy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFhLFFBQUEsWUFBWSxHQUFXLEtBQUssQ0FBQztBQVMxQyxTQUFnQiwrQkFBK0IsQ0FBQyxrQkFBMEM7SUFDekYsUUFBUSxrQkFBa0IsRUFBRTtRQUMzQixLQUFLLFFBQVE7WUFDWixPQUFPLElBQUksQ0FBQztRQUNiLEtBQUssV0FBVztZQUNmLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7SUFDRCxNQUFNLElBQUksS0FBSyxDQUNkLHFEQUFxRCxrQkFBa0IsOENBQThDLENBQ3JILENBQUM7QUFDSCxDQUFDO0FBVkQsMEVBVUM7QUFFRCxTQUFnQiwyQkFBMkIsQ0FDMUMsS0FBYSxFQUNiLHFCQUE0QyxFQUM1QyxXQUFvQixLQUFLO0lBRXpCLFFBQVEscUJBQXFCLEVBQUU7UUFDOUIsS0FBSyxjQUFjLENBQUMsQ0FBQztZQUNwQixJQUFJLE1BQU0sR0FBVyxFQUFFLENBQUM7WUFDeEIsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFDO1lBQ3RCLEtBQUssYUFBYSxFQUFFLGFBQWEsR0FBRyxLQUFLLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxhQUFhLENBQUMsS0FBSyxHQUFHLEVBQUUsYUFBYSxFQUFFLEVBQUU7Z0JBQ2xHLE1BQU0sSUFBSSxHQUFHLENBQUM7YUFDZDtZQUNELE1BQU0sSUFBSSxLQUFLO2lCQUNiLEtBQUssQ0FBQyxhQUFhLENBQUM7aUJBQ3BCLElBQUksRUFBRTtpQkFDTixPQUFPLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3pCLE9BQU8sTUFBTSxDQUFDO1NBQ2Q7UUFDRCxLQUFLLFVBQVUsQ0FBQyxDQUFDO1lBQ2hCLElBQUksTUFBTSxHQUFXLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDdkMsSUFBSSxDQUFDLFFBQVEsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRyxFQUFFO2dCQUNsQyxNQUFNLEdBQUcsSUFBSSxNQUFNLEVBQUUsQ0FBQzthQUN0QjtZQUNELE9BQU8sTUFBTSxDQUFDO1NBQ2Q7UUFDRCxLQUFLLFVBQVUsQ0FBQztRQUNoQjtZQUVDLE9BQU8sS0FBSyxDQUFDO0tBQ2Q7QUFDRixDQUFDO0FBL0JELGtFQStCQztBQUVZLFFBQUEsT0FBTyxHQUFHO0lBQ3RCLGtCQUFrQixFQUFFO1FBQ25CLEtBQUssRUFBRSxPQUFPO1FBQ2QsUUFBUSxFQUFFLG9CQUFZO1FBQ3RCLElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLFFBQVE7UUFDakIsV0FBVyxFQUFFLHlEQUF5RDtRQUN0RSxPQUFPLEVBQUU7WUFDUjtnQkFDQyxLQUFLLEVBQUUsUUFBUTtnQkFDZixXQUFXLEVBQ1Ysc0dBQXNHO2FBQ3ZHO1lBQ0Q7Z0JBQ0MsS0FBSyxFQUFFLFdBQVc7Z0JBQ2xCLFdBQVcsRUFDVixnSEFBZ0g7YUFDakg7U0FDRDtLQUNEO0lBQ0QscUJBQXFCLEVBQUU7UUFDdEIsS0FBSyxFQUFFLE9BQU87UUFDZCxRQUFRLEVBQUUsb0JBQVk7UUFDdEIsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsVUFBVTtRQUNuQixXQUFXLEVBQUUsNENBQTRDO1FBQ3pELE9BQU8sRUFBRTtZQUNSO2dCQUNDLEtBQUssRUFBRSxVQUFVO2dCQUNqQixXQUFXLEVBQUUsMEVBQTBFO2FBQ3ZGO1lBQ0Q7Z0JBQ0MsS0FBSyxFQUFFLGNBQWM7Z0JBQ3JCLFdBQVcsRUFBRSx5RUFBeUU7YUFDdEY7WUFDRDtnQkFDQyxLQUFLLEVBQUUsVUFBVTtnQkFDakIsV0FBVyxFQUFFLGtFQUFrRTthQUMvRTtTQUNEO0tBQ0Q7Q0FDRCxDQUFDIn0= |
{ | ||
"name": "@prettier/plugin-pug", | ||
"version": "1.1.5", | ||
"version": "1.1.6", | ||
"description": "Prettier Pug Plugin", | ||
@@ -34,15 +34,15 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@types/jest": "~24.9.0", | ||
"@types/node": "~13.1.8", | ||
"@types/jest": "~25.1.2", | ||
"@types/node": "~13.7.0", | ||
"@types/prettier": "~1.19.0", | ||
"@typescript-eslint/eslint-plugin": "~2.17.0", | ||
"@typescript-eslint/parser": "~2.17.0", | ||
"@typescript-eslint/eslint-plugin": "~2.19.0", | ||
"@typescript-eslint/parser": "~2.19.0", | ||
"benchmark": "~2.1.4", | ||
"eslint": "~6.8.0", | ||
"eslint-config-prettier": "~6.9.0", | ||
"eslint-config-prettier": "~6.10.0", | ||
"eslint-plugin-prettier": "~3.1.2", | ||
"jest": "~24.9.0", | ||
"jest": "~25.1.0", | ||
"jest-junit": "~10.0.0", | ||
"prettier": "1.19.1", | ||
"ts-jest": "~24.3.0", | ||
"ts-jest": "~25.2.0", | ||
"typescript": "~3.7.5" | ||
@@ -49,0 +49,0 @@ }, |
@@ -225,1 +225,3 @@ <p align="center"> | ||
Many thanks also to [@j-f1](https://github.com/j-f1), [@lipis](https://github.com/lipis) and [@azz](https://github.com/azz) for the help in transferring this repos to the prettier orga. | ||
Thanks to [@Peilonrayz](https://github.com/Peilonrayz), who gave me the [idea](https://codereview.stackexchange.com/a/236031/128216) to rewrite the printer into a [class](https://github.com/prettier/plugin-pug/commit/a6e3a4b776ce67f0d5d763aaf1f88c0c860c6ed3) and thus make the code a lot more maintainable. |
711
src/index.ts
@@ -1,15 +0,8 @@ | ||
import { Doc, FastPath, format, Options, Parser, ParserOptions, Plugin, util } from 'prettier'; | ||
import { Doc, FastPath, Options, Parser, ParserOptions, Plugin } from 'prettier'; | ||
import * as lex from 'pug-lexer'; | ||
import { AttributeToken, EndAttributesToken, Token } from 'pug-lexer'; | ||
import { DOCTYPE_SHORTCUT_REGISTRY } from './doctype-shortcut-registry'; | ||
import { Token } from 'pug-lexer'; | ||
import { createLogger, Logger, LogLevel } from './logger'; | ||
import { | ||
formatCommentPreserveSpaces, | ||
options as pugOptions, | ||
PugParserOptions, | ||
resolveAttributeSeparatorOption | ||
} from './options'; | ||
import { options as pugOptions, PugParserOptions } from './options'; | ||
import { PugPrinter } from './printer'; | ||
const { makeString } = util; | ||
const logger: Logger = createLogger(console); | ||
@@ -20,67 +13,2 @@ if (process.env.NODE_ENV === 'test') { | ||
function previousNormalAttributeToken(tokens: Token[], index: number): AttributeToken | undefined { | ||
for (let i: number = index - 1; i > 0; i--) { | ||
const token: Token = tokens[i]; | ||
if (token.type === 'start-attributes') { | ||
return; | ||
} | ||
if (token.type === 'attribute') { | ||
if (token.name !== 'class' && token.name !== 'id') { | ||
return token; | ||
} | ||
} | ||
} | ||
return; | ||
} | ||
function printIndent(previousToken: Token, indent: string, indentLevel: number): string { | ||
switch (previousToken?.type) { | ||
case 'newline': | ||
case 'outdent': | ||
return indent.repeat(indentLevel); | ||
case 'indent': | ||
return indent; | ||
} | ||
return ''; | ||
} | ||
function formatText(text: string, singleQuote: boolean): string { | ||
let result: string = ''; | ||
while (text) { | ||
const start = text.indexOf('{{'); | ||
if (start !== -1) { | ||
result += text.slice(0, start); | ||
text = text.substring(start + 2); | ||
const end = text.indexOf('}}'); | ||
if (end !== -1) { | ||
let code = text.slice(0, end); | ||
code = code.trim(); | ||
code = format(code, { parser: 'babel', singleQuote: !singleQuote, printWidth: 9000 }); | ||
if (code.endsWith(';\n')) { | ||
code = code.slice(0, -2); | ||
} | ||
result += `{{ ${code} }}`; | ||
text = text.slice(end + 2); | ||
} else { | ||
result += '{{'; | ||
result += text; | ||
text = ''; | ||
} | ||
} else { | ||
result += text; | ||
text = ''; | ||
} | ||
} | ||
return result; | ||
} | ||
function unwrapLineFeeds(value: string): string { | ||
return value.includes('\n') | ||
? value | ||
.split('\n') | ||
.map((part) => part.trim()) | ||
.join('') | ||
: value; | ||
} | ||
export const plugin: Plugin = { | ||
@@ -144,622 +72,13 @@ languages: [ | ||
const tokens: Token[] = path.stack[0]; | ||
let result: string = ''; | ||
let indentLevel: number = 0; | ||
const indent: string = useTabs ? '\t' : ' '.repeat(tabWidth); | ||
let pipelessText: boolean = false; | ||
let pipelessComment: boolean = false; | ||
const alwaysUseAttributeSeparator: boolean = resolveAttributeSeparatorOption(attributeSeparator); | ||
let possibleIdPosition: number = 0; | ||
let possibleClassPosition: number = 0; | ||
let previousAttributeRemapped: boolean = false; | ||
let wrapAttributes: boolean = false; | ||
const codeInterpolationOptions: Options = { | ||
singleQuote: !singleQuote, | ||
printWidth: 9000, | ||
endOfLine: 'lf' | ||
}; | ||
if (tokens[0]?.type === 'text') { | ||
result += '| '; | ||
} | ||
for (let index: number = 0; index < tokens.length; index++) { | ||
const token: Token = tokens[index]; | ||
const previousToken: Token | undefined = tokens[index - 1]; | ||
const nextToken: Token | undefined = tokens[index + 1]; | ||
logger.debug('[printers:pug-ast:print]:', JSON.stringify(token)); | ||
switch (token.type) { | ||
case 'tag': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
if (!(token.val === 'div' && (nextToken.type === 'class' || nextToken.type === 'id'))) { | ||
result += token.val; | ||
} | ||
possibleIdPosition = result.length; | ||
possibleClassPosition = result.length; | ||
break; | ||
case 'start-attributes': | ||
if (nextToken?.type === 'attribute') { | ||
previousAttributeRemapped = false; | ||
possibleClassPosition = result.length; | ||
result += '('; | ||
const start: number = result.lastIndexOf('\n') + 1; | ||
let lineLength: number = result.substring(start).length; | ||
logger.debug(lineLength, printWidth); | ||
let tempToken: AttributeToken | EndAttributesToken = nextToken; | ||
let tempIndex: number = index + 1; | ||
while (tempToken.type === 'attribute') { | ||
lineLength += tempToken.name.length + 1 + tempToken.val.toString().length; | ||
logger.debug(lineLength, printWidth); | ||
tempToken = tokens[++tempIndex] as AttributeToken | EndAttributesToken; | ||
} | ||
if (lineLength > printWidth) { | ||
wrapAttributes = true; | ||
} | ||
} | ||
break; | ||
case 'attribute': { | ||
if (typeof token.val === 'string') { | ||
const surroundedByQuotes: boolean = | ||
(token.val.startsWith('"') && token.val.endsWith('"')) || | ||
(token.val.startsWith("'") && token.val.endsWith("'")); | ||
if (surroundedByQuotes) { | ||
if (token.name === 'class') { | ||
// Handle class attribute | ||
let val = token.val; | ||
val = val.substring(1, val.length - 1); | ||
val = val.trim(); | ||
val = val.replace(/\s\s+/g, ' '); | ||
const classes: string[] = val.split(' '); | ||
const specialClasses: string[] = []; | ||
const validClassNameRegex: RegExp = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/; | ||
for (const className of classes) { | ||
if (!validClassNameRegex.test(className)) { | ||
specialClasses.push(className); | ||
continue; | ||
} | ||
// Write css-class in front of attributes | ||
const position: number = possibleClassPosition; | ||
result = [ | ||
result.slice(0, position), | ||
`.${className}`, | ||
result.slice(position) | ||
].join(''); | ||
possibleClassPosition += 1 + className.length; | ||
result = result.replace(/div\./, '.'); | ||
} | ||
if (specialClasses.length > 0) { | ||
token.val = makeString( | ||
specialClasses.join(' '), | ||
singleQuote ? "'" : '"', | ||
false | ||
); | ||
previousAttributeRemapped = false; | ||
} else { | ||
previousAttributeRemapped = true; | ||
break; | ||
} | ||
} else if (token.name === 'id') { | ||
// Handle id attribute | ||
let val = token.val; | ||
val = val.substring(1, val.length - 1); | ||
val = val.trim(); | ||
const validIdNameRegex: RegExp = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/; | ||
if (!validIdNameRegex.test(val)) { | ||
val = makeString(val, singleQuote ? "'" : '"', false); | ||
result += `id=${val}`; | ||
break; | ||
} | ||
// Write css-id in front of css-classes | ||
const position: number = possibleIdPosition; | ||
result = [result.slice(0, position), `#${val}`, result.slice(position)].join( | ||
'' | ||
); | ||
possibleClassPosition += 1 + val.length; | ||
result = result.replace(/div#/, '#'); | ||
if (previousToken.type === 'attribute' && previousToken.name !== 'class') { | ||
previousAttributeRemapped = true; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
const hasNormalPreviousToken: AttributeToken | undefined = previousNormalAttributeToken( | ||
tokens, | ||
index | ||
); | ||
if ( | ||
previousToken?.type === 'attribute' && | ||
(!previousAttributeRemapped || hasNormalPreviousToken) | ||
) { | ||
if (alwaysUseAttributeSeparator || /^(\(|\[|:).*/.test(token.name)) { | ||
result += ','; | ||
} | ||
if (!wrapAttributes) { | ||
result += ' '; | ||
} | ||
} | ||
previousAttributeRemapped = false; | ||
if (wrapAttributes) { | ||
result += '\n'; | ||
result += indent.repeat(indentLevel + 1); | ||
} | ||
result += `${token.name}`; | ||
if (typeof token.val === 'boolean') { | ||
if (token.val !== true) { | ||
result += `=${token.val}`; | ||
} | ||
} else { | ||
let val = token.val; | ||
if (/^((v-bind|v-on|v-slot)?:|v-model|v-on|@).*/.test(token.name)) { | ||
// Format Vue expression | ||
val = val.trim(); | ||
val = val.slice(1, -1); | ||
val = format(val, { | ||
parser: '__vue_expression' as any, | ||
...codeInterpolationOptions | ||
}); | ||
val = unwrapLineFeeds(val); | ||
const quotes: "'" | '"' = singleQuote ? "'" : '"'; | ||
val = `${quotes}${val}${quotes}`; | ||
} else if (/^(\(.*\)|\[.*\])$/.test(token.name)) { | ||
// Format Angular action or binding | ||
val = val.trim(); | ||
val = val.slice(1, -1); | ||
val = format(val, { | ||
parser: '__ng_interpolation' as any, | ||
...codeInterpolationOptions | ||
}); | ||
val = unwrapLineFeeds(val); | ||
const quotes: "'" | '"' = singleQuote ? "'" : '"'; | ||
val = `${quotes}${val}${quotes}`; | ||
} else if (/^\*.*$/.test(token.name)) { | ||
// Format Angular directive | ||
val = val.trim(); | ||
val = val.slice(1, -1); | ||
val = format(val, { parser: '__ng_directive' as any, ...codeInterpolationOptions }); | ||
const quotes: "'" | '"' = singleQuote ? "'" : '"'; | ||
val = `${quotes}${val}${quotes}`; | ||
} else if (/^(["']{{)(.*)(}}["'])$/.test(val)) { | ||
// Format Angular interpolation | ||
val = val.slice(3, -3); | ||
val = val.trim(); | ||
val = val.replace(/\s\s+/g, ' '); | ||
// val = format(val, { | ||
// parser: '__ng_interpolation' as any, | ||
// ...codeInterpolationOptions | ||
// }); | ||
const quotes: "'" | '"' = singleQuote ? "'" : '"'; | ||
val = `${quotes}{{ ${val} }}${quotes}`; | ||
} else if (/^["'](.*)["']$/.test(val)) { | ||
val = makeString(val.slice(1, -1), singleQuote ? "'" : '"', false); | ||
} else if (val === 'true') { | ||
// The value is exactly true and is not quoted | ||
break; | ||
} else if (token.mustEscape) { | ||
val = format(val, { | ||
parser: '__js_expression' as any, | ||
...codeInterpolationOptions | ||
}); | ||
} else { | ||
// The value is not quoted and may be js-code | ||
val = val.trim(); | ||
val = val.replace(/\s\s+/g, ' '); | ||
if (val.startsWith('{ ')) { | ||
val = `{${val.substring(2, val.length)}`; | ||
} | ||
} | ||
if (token.mustEscape === false) { | ||
result += '!'; | ||
} | ||
result += `=${val}`; | ||
} | ||
break; | ||
} | ||
case 'end-attributes': | ||
if (wrapAttributes) { | ||
result += '\n'; | ||
result += indent.repeat(indentLevel); | ||
} | ||
wrapAttributes = false; | ||
if (result.endsWith('(')) { | ||
// There were no attributes | ||
result = result.substring(0, result.length - 1); | ||
} else if (previousToken?.type === 'attribute') { | ||
result += ')'; | ||
} | ||
if (nextToken?.type === 'text' || nextToken?.type === 'path') { | ||
result += ' '; | ||
} | ||
break; | ||
case 'indent': | ||
result += '\n'; | ||
result += indent.repeat(indentLevel); | ||
indentLevel++; | ||
break; | ||
case 'outdent': | ||
if (previousToken?.type !== 'outdent') { | ||
if (token.loc.start.line - previousToken.loc.end.line > 1) { | ||
// Insert one extra blank line | ||
result += '\n'; | ||
} | ||
result += '\n'; | ||
} | ||
indentLevel--; | ||
break; | ||
case 'class': | ||
switch (previousToken?.type) { | ||
case 'newline': | ||
case 'outdent': | ||
case 'indent': { | ||
const prefix = result.slice(0, result.length); | ||
const _indent = printIndent(previousToken, indent, indentLevel); | ||
const val = `.${token.val}`; | ||
result = [prefix, _indent, val, result.slice(result.length)].join(''); | ||
possibleClassPosition = prefix.length + _indent.length + val.length; | ||
break; | ||
} | ||
default: { | ||
const prefix = result.slice(0, possibleClassPosition); | ||
const val = `.${token.val}`; | ||
result = [prefix, val, result.slice(possibleClassPosition)].join(''); | ||
possibleClassPosition = prefix.length + val.length; | ||
break; | ||
} | ||
} | ||
if (nextToken?.type === 'text') { | ||
result += ' '; | ||
} | ||
break; | ||
case 'eos': | ||
// Remove all newlines at the end | ||
while (result.endsWith('\n')) { | ||
result = result.substring(0, result.length - 1); | ||
} | ||
// Insert one newline | ||
result += '\n'; | ||
break; | ||
case 'comment': { | ||
result += printIndent(previousToken, indent, indentLevel); | ||
if (previousToken && !['newline', 'indent', 'outdent'].includes(previousToken.type)) { | ||
result += ' '; | ||
} | ||
result += '//'; | ||
if (!token.buffer) { | ||
result += '-'; | ||
} | ||
result += formatCommentPreserveSpaces(token.val, commentPreserveSpaces); | ||
if (nextToken.type === 'start-pipeless-text') { | ||
pipelessComment = true; | ||
} | ||
break; | ||
} | ||
case 'newline': | ||
if (previousToken && token.loc.start.line - previousToken.loc.end.line > 1) { | ||
// Insert one extra blank line | ||
result += '\n'; | ||
} | ||
result += '\n'; | ||
break; | ||
case 'text': { | ||
let val = token.val; | ||
let needsTrailingWhitespace: boolean = false; | ||
if (pipelessText) { | ||
switch (previousToken?.type) { | ||
case 'newline': | ||
result += indent.repeat(indentLevel + 1); | ||
break; | ||
case 'start-pipeless-text': | ||
result += indent; | ||
break; | ||
} | ||
if (pipelessComment) { | ||
val = formatCommentPreserveSpaces(val, commentPreserveSpaces, true); | ||
} | ||
} else { | ||
if (nextToken && val.endsWith(' ')) { | ||
switch (nextToken.type) { | ||
case 'interpolated-code': | ||
case 'start-pug-interpolation': | ||
needsTrailingWhitespace = true; | ||
break; | ||
} | ||
} | ||
val = val.replace(/\s\s+/g, ' '); | ||
switch (previousToken?.type) { | ||
case 'newline': | ||
result += indent.repeat(indentLevel); | ||
if (/^ .+$/.test(val)) { | ||
result += '|\n'; | ||
result += indent.repeat(indentLevel); | ||
} | ||
result += '|'; | ||
if (/.*\S.*/.test(token.val) || nextToken?.type === 'start-pug-interpolation') { | ||
result += ' '; | ||
} | ||
break; | ||
case 'indent': | ||
result += indent; | ||
result += '|'; | ||
if (/.*\S.*/.test(token.val)) { | ||
result += ' '; | ||
} | ||
break; | ||
case 'interpolated-code': | ||
case 'end-pug-interpolation': | ||
if (/^ .+$/.test(val)) { | ||
result += ' '; | ||
} | ||
break; | ||
} | ||
val = val.trim(); | ||
val = formatText(val, singleQuote); | ||
val = val.replace(/#(\{|\[)/g, '\\#$1'); | ||
} | ||
if ( | ||
['tag', 'id', 'interpolation', 'call', '&attributes', 'filter'].includes( | ||
previousToken?.type | ||
) | ||
) { | ||
val = ` ${val}`; | ||
} | ||
result += val; | ||
if (needsTrailingWhitespace) { | ||
result += ' '; | ||
} | ||
break; | ||
} | ||
case 'interpolated-code': | ||
switch (previousToken?.type) { | ||
case 'tag': | ||
case 'class': | ||
case 'id': | ||
case 'end-attributes': | ||
result += ' '; | ||
break; | ||
case 'start-pug-interpolation': | ||
result += '| '; | ||
break; | ||
case 'indent': | ||
case 'newline': | ||
case 'outdent': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += '| '; | ||
break; | ||
} | ||
result += token.mustEscape ? '#' : '!'; | ||
result += `{${token.val}}`; | ||
break; | ||
case 'code': { | ||
result += printIndent(previousToken, indent, indentLevel); | ||
if (!token.mustEscape && token.buffer) { | ||
result += '!'; | ||
} | ||
result += token.buffer ? '=' : '-'; | ||
let useSemi = semi; | ||
if (useSemi && (token.mustEscape || token.buffer)) { | ||
useSemi = false; | ||
} | ||
let val = token.val; | ||
try { | ||
const valBackup = val; | ||
val = format(val, { | ||
parser: 'babel', | ||
...codeInterpolationOptions, | ||
semi: useSemi, | ||
endOfLine: 'lf' | ||
}); | ||
val = val.slice(0, -1); | ||
if (val.includes('\n')) { | ||
val = valBackup; | ||
} | ||
} catch (error) { | ||
logger.warn(error); | ||
} | ||
result += ` ${val}`; | ||
break; | ||
} | ||
case 'id': { | ||
switch (previousToken?.type) { | ||
case 'newline': | ||
case 'outdent': | ||
case 'indent': { | ||
const prefix = result.slice(0, result.length); | ||
const _indent = printIndent(previousToken, indent, indentLevel); | ||
const val = `#${token.val}`; | ||
result = [prefix, _indent, val, result.slice(result.length)].join(''); | ||
possibleClassPosition = prefix.length + _indent.length + val.length; | ||
break; | ||
} | ||
default: { | ||
const prefix = result.slice(0, possibleIdPosition); | ||
const val = `#${token.val}`; | ||
result = [prefix, val, result.slice(possibleIdPosition)].join(''); | ||
possibleClassPosition = prefix.length + val.length; | ||
break; | ||
} | ||
} | ||
break; | ||
} | ||
case 'start-pipeless-text': | ||
pipelessText = true; | ||
result += '\n'; | ||
result += indent.repeat(indentLevel); | ||
break; | ||
case 'end-pipeless-text': | ||
pipelessText = false; | ||
pipelessComment = false; | ||
break; | ||
case 'doctype': | ||
result += 'doctype'; | ||
if (token.val) { | ||
result += ` ${token.val}`; | ||
} | ||
break; | ||
case 'dot': | ||
result += '.'; | ||
break; | ||
case 'block': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += 'block '; | ||
if (token.mode !== 'replace') { | ||
result += token.mode; | ||
result += ' '; | ||
} | ||
result += token.val; | ||
break; | ||
case 'extends': | ||
result += 'extends '; | ||
break; | ||
case 'path': | ||
if (['include', 'filter'].includes(previousToken?.type)) { | ||
result += ' '; | ||
} | ||
result += token.val; | ||
break; | ||
case 'start-pug-interpolation': | ||
result += '#['; | ||
break; | ||
case 'end-pug-interpolation': | ||
result += ']'; | ||
break; | ||
case 'interpolation': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `#{${token.val}}`; | ||
possibleIdPosition = result.length; | ||
possibleClassPosition = result.length; | ||
break; | ||
case 'include': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += 'include'; | ||
break; | ||
case 'filter': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `:${token.val}`; | ||
break; | ||
case 'call': { | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `+${token.val}`; | ||
let args: string | null = token.args; | ||
if (args) { | ||
args = args.trim(); | ||
args = args.replace(/\s\s+/g, ' '); | ||
result += `(${args})`; | ||
} | ||
possibleIdPosition = result.length; | ||
possibleClassPosition = result.length; | ||
break; | ||
} | ||
case 'mixin': { | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `mixin ${token.val}`; | ||
let args: string | null = token.args; | ||
if (args) { | ||
args = args.trim(); | ||
args = args.replace(/\s\s+/g, ' '); | ||
result += `(${args})`; | ||
} | ||
break; | ||
} | ||
case 'if': { | ||
result += printIndent(previousToken, indent, indentLevel); | ||
const match = /^!\((.*)\)$/.exec(token.val); | ||
logger.debug(match); | ||
result += !match ? `if ${token.val}` : `unless ${match[1]}`; | ||
break; | ||
} | ||
case 'mixin-block': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += 'block'; | ||
break; | ||
case 'else': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += 'else'; | ||
break; | ||
case '&attributes': | ||
result += `&attributes(${token.val})`; | ||
break; | ||
case 'text-html': { | ||
result += printIndent(previousToken, indent, indentLevel); | ||
const match: RegExpExecArray | null = /^<(.*?)>(.*)<\/(.*?)>$/.exec(token.val); | ||
logger.debug(match); | ||
if (match) { | ||
result += `${match[1]} ${match[2]}`; | ||
break; | ||
} | ||
const entry = Object.entries(DOCTYPE_SHORTCUT_REGISTRY).find( | ||
([key]) => key === token.val.toLowerCase() | ||
); | ||
if (entry) { | ||
result += entry[1]; | ||
break; | ||
} | ||
result += token.val; | ||
break; | ||
} | ||
case 'each': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `each ${token.val}`; | ||
if (token.key !== null) { | ||
result += `, ${token.key}`; | ||
} | ||
result += ` in ${token.code}`; | ||
break; | ||
case 'while': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `while ${token.val}`; | ||
break; | ||
case 'case': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `case ${token.val}`; | ||
break; | ||
case 'when': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `when ${token.val}`; | ||
break; | ||
case ':': | ||
result += ': '; | ||
possibleIdPosition = result.length; | ||
possibleClassPosition = result.length; | ||
break; | ||
case 'default': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += 'default'; | ||
break; | ||
case 'else-if': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += `else if ${token.val}`; | ||
break; | ||
case 'blockcode': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += '-'; | ||
break; | ||
case 'yield': | ||
result += printIndent(previousToken, indent, indentLevel); | ||
result += 'yield'; | ||
break; | ||
case 'slash': | ||
result += '/'; | ||
break; | ||
default: | ||
throw new Error('Unhandled token: ' + JSON.stringify(token)); | ||
} | ||
} | ||
logger.debug(result); | ||
const printer = new PugPrinter(tokens, { | ||
printWidth, | ||
singleQuote, | ||
tabWidth, | ||
useTabs, | ||
attributeSeparator, | ||
commentPreserveSpaces, | ||
semi | ||
}); | ||
const result = printer.build(); | ||
logger.debug('[printers:pug-ast:print]:', result); | ||
return result; | ||
@@ -766,0 +85,0 @@ }, |
@@ -35,3 +35,3 @@ export const CATEGORY_PUG: string = 'Pug'; | ||
result += input | ||
.substring(firstNonSpace) | ||
.slice(firstNonSpace) | ||
.trim() | ||
@@ -44,3 +44,3 @@ .replace(/\s\s+/g, ' '); | ||
result = result.replace(/\s\s+/g, ' '); | ||
if (!pipeless && input.startsWith(' ')) { | ||
if (!pipeless && input[0] === ' ') { | ||
result = ` ${result}`; | ||
@@ -47,0 +47,0 @@ } |
@@ -0,1 +1,2 @@ | ||
/* eslint-disable @typescript-eslint/member-ordering */ | ||
declare module 'pug-lexer' { | ||
@@ -2,0 +3,0 @@ namespace lex { |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
146460
22
2507
227
2
1