@yugu/gogocode
Version:
The simplest tool to parse/transform/generate code on ast
124 lines (106 loc) • 4.26 kB
JavaScript
const indentString = require('indent-string');
const jsGenerate = require('../js-core/generate')
const htmlGenerate = require('../html-core/serialize-node')
/**
* The following function is adapted from https://github.com/psalaets/vue-sfc-descriptor-to-string/blob/master/index.js
*/
/**
* The MIT License (MIT)
* Copyright (c) 2018 Paul Salaets
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
module.exports = function toString(sfcDescriptor, options = {}) {
let { template, script, scriptSetup, styles = [], customBlocks = [], templateAst, scriptAst, scriptSetupAst } = sfcDescriptor;
if (templateAst && templateAst.node) {
template.content = htmlGenerate(templateAst.node);
}
if (scriptAst && scriptAst.node) {
script.content = jsGenerate(scriptAst.node);
}
if (scriptSetupAst && scriptSetupAst.node) {
scriptSetup.content = jsGenerate(scriptSetupAst.node);
}
const indents = Object.assign(
{
template: 2,
script: 0,
style: 0
},
options.indents
);
return (
[template, script, scriptSetup, ...styles, ...customBlocks]
// discard blocks that don't exist
.filter((block) => block != null)
// sort blocks by source position
.sort((a, b) => a.start - b.start)
// figure out exact source positions of blocks
.map((block) => {
const openTag = makeOpenTag(block);
const closeTag = makeCloseTag(block);
return Object.assign({}, block, {
openTag,
closeTag,
startOfOpenTag: block.start - openTag.length,
endOfOpenTag: block.start,
startOfCloseTag: block.end,
endOfCloseTag: block.end + closeTag.length
});
})
// generate sfc source
.reduce((sfcCode, block, index, array) => {
const first = index === 0;
let newlinesBefore = 0;
if (first) {
newlinesBefore = block.startOfOpenTag;
} else {
const prevBlock = array[index - 1];
newlinesBefore =
block.startOfOpenTag - prevBlock.endOfCloseTag;
}
newlinesBefore = newlinesBefore || 1
return (
sfcCode +
'\n'.repeat(newlinesBefore) +
block.openTag +
indentString(block.content, indents[block.type] || 0) +
block.closeTag
);
}, '')
);
}
function makeOpenTag(block) {
let source = '<' + block.type;
source += Object.keys(block.attrs)
.sort()
.map((name) => {
const value = block.attrs[name];
if (value === true) {
return name;
} else {
return `${name}="${value}"`;
}
})
.map((attr) => ' ' + attr)
.join('');
return source + '>';
}
function makeCloseTag(block) {
return `</${block.type}>\n`;
}