webmat
Version:
Formats your entire project with clang-format
139 lines • 5.44 kB
JavaScript
;
/**
* @license
* Copyright (c) 2018 Google Inc. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* Code distributed by Google as part of this project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
Object.defineProperty(exports, "__esModule", { value: true });
const dom5 = require("dom5");
const esprima = require("esprima");
const fs = require("fs");
const parse5 = require("parse5");
const SINGLE_TAB = ' ';
/**
* Indents and writes the content to the file.
*
* @param formattedContent Content to be written to the file.
*/
async function writeTofile(formattedContent) {
const writableStream = fs.createWriteStream(formattedContent.filePath);
await updateAst(formattedContent);
const formattedDom = generateDom(formattedContent);
writableStream.write(formattedDom);
writableStream.end();
}
exports.writeTofile = writeTofile;
/**
* Tabs the given content and writes it to the AST in the content chunks
*
* @param formattedContent Content to be written to the AST
*/
function updateAst(formattedContent) {
const updatePromises = [];
for (const chunk of formattedContent.contents) {
const nodeLocation = chunk.node.__location;
let numTabs = 1;
if (nodeLocation.col) {
numTabs = Math.floor(nodeLocation.col / SINGLE_TAB.length) + 1;
}
const tabbedString = SINGLE_TAB.repeat(numTabs);
const updatePromise = chunk.streamReader.streamCached.then((data) => {
const allTokens = esprima.tokenize(data, { loc: true });
const templateTokens = allTokens.filter((token) => {
return token.type === 'Template';
});
let stringifiedContents = '';
let splitData = data.split('\n');
const nonIndentableLines = [];
for (const templateToken of templateTokens) {
const firstNonIndentableLine = templateToken.loc.start.line;
const lastNonIndentableLine = templateToken.loc.end.line;
// just indent if it is all the same line
if (templateToken.loc.start.line !== templateToken.loc.end.line) {
nonIndentableLines.push({
start: firstNonIndentableLine,
end: lastNonIndentableLine,
});
}
}
// only tab nonempty lines and non-templates
for (let lineNumber = 1; lineNumber <= splitData.length; lineNumber++) {
const index = lineNumber - 1;
let line = splitData[index];
let isIndentable = true;
for (const nonIndentableLine of nonIndentableLines) {
// first line of a template string is indentable
if (lineNumber > nonIndentableLine.start &&
lineNumber <= nonIndentableLine.end) {
isIndentable = false;
break;
}
}
if (line.length && isIndentable) {
line = `${tabbedString}${line}`;
}
splitData[index] = line;
}
splitData = splitData.map((line) => {
return line;
});
const tabbedData = splitData.join(`\n`);
stringifiedContents += tabbedData;
const trimmedContents = stringifiedContents.trim();
// deal with tabs at the beginning and end if the script is not empty
if (stringifiedContents) {
stringifiedContents = `\n${tabbedString}${trimmedContents}` +
`\n${SINGLE_TAB.repeat(numTabs - 1)}`;
}
dom5.setTextContent(chunk.node, stringifiedContents);
});
updatePromises.push(updatePromise);
}
return Promise.all(updatePromises);
}
/**
* Transforms the content into a dom string with prpoper spacing around the
* spliced inline scripts.
*
* @param formattedContent Content to be transformed into a dom string.
*/
function generateDom(formattedContent) {
const sortedChunks = formattedContent.contents.sort((a, b) => {
const aLine = a.node.__location.startTag.line;
const bLine = b.node.__location.startTag.line;
if (aLine > bLine) {
return -1;
}
else if (aLine < bLine) {
return 1;
}
return 0;
});
const splitDom = formattedContent.dom.split('\n');
for (const chunk of sortedChunks) {
const location = chunk.node.__location;
const startTag = location.startTag;
const endTag = location.endTag;
const numLines = endTag.line - startTag.line + 1;
const scriptTagWhitespace = {
attrs: [],
nodeName: '#text',
value: ' '.repeat(startTag.col - 1),
__location: {}
};
const fakeNode = {
attrs: [],
childNodes: [scriptTagWhitespace, chunk.node],
nodeName: 'div',
__location: {}
};
const formattedChunk = parse5.serialize(fakeNode);
splitDom.splice(startTag.line - 1, numLines, formattedChunk);
}
return splitDom.join('\n');
}
//# sourceMappingURL=write-output.js.map