mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
268 lines
6.8 KiB
JavaScript
268 lines
6.8 KiB
JavaScript
'use strict';
|
|
|
|
const isArray = Array.isArray;
|
|
const Node = require('./ast/Node');
|
|
const Literal = require('./ast/Literal');
|
|
const Identifier = require('./ast/Identifier');
|
|
const ok = require('assert').ok;
|
|
const Container = require('./ast/Container');
|
|
const isValidJavaScriptVarName = require('./util/isValidJavaScriptVarName');
|
|
|
|
class CodeWriter {
|
|
constructor(options) {
|
|
options = options || {};
|
|
this.root = null;
|
|
this._indentStr = options.indent != null ? options.indent : ' ';
|
|
this._indentSize = this._indentStr.length;
|
|
|
|
this._code = '';
|
|
this.currentIndent = '';
|
|
|
|
this.outputType = options.output || 'html';
|
|
|
|
this._writeCodeMethodName = 'write' +
|
|
this.outputType.charAt(0).toUpperCase() +
|
|
this.outputType.substring(1) +
|
|
'Code';
|
|
}
|
|
|
|
getCode() {
|
|
return this._code;
|
|
}
|
|
|
|
writeBlock(body) {
|
|
if (!body) {
|
|
this.write('{}');
|
|
return;
|
|
}
|
|
|
|
if (typeof body === 'function') {
|
|
body = body();
|
|
}
|
|
|
|
if (!body ||
|
|
(Array.isArray(body) && body.length === 0) ||
|
|
(body instanceof Container && body.length === 0)) {
|
|
this.write('{}');
|
|
return;
|
|
}
|
|
|
|
this.write('{\n')
|
|
.incIndent();
|
|
|
|
this.writeStatements(body);
|
|
|
|
this.decIndent()
|
|
.writeLineIndent()
|
|
.write('}');
|
|
}
|
|
|
|
writeStatements(nodes) {
|
|
if (!nodes) {
|
|
return;
|
|
}
|
|
|
|
ok(nodes, '"nodes" expected');
|
|
let firstStatement = true;
|
|
|
|
var writeNode = (node) => {
|
|
if (Array.isArray(node) || (node instanceof Container)) {
|
|
node.forEach(writeNode);
|
|
return;
|
|
} else {
|
|
if (firstStatement) {
|
|
firstStatement = false;
|
|
} else {
|
|
this._write('\n');
|
|
}
|
|
|
|
this.writeLineIndent();
|
|
|
|
if (typeof node === 'string') {
|
|
this._write(node);
|
|
} else {
|
|
node.statement = true;
|
|
this.write(node);
|
|
}
|
|
|
|
if (this._code.endsWith('\n')) {
|
|
// Do nothing
|
|
} else if (this._code.endsWith(';')) {
|
|
this._code += '\n';
|
|
} else if (this._code.endsWith('\n' + this.currentIndent)) {
|
|
// Do nothing
|
|
} else {
|
|
this._code += ';\n';
|
|
}
|
|
}
|
|
};
|
|
|
|
if (nodes instanceof Node) {
|
|
writeNode(nodes);
|
|
} else {
|
|
nodes.forEach(writeNode);
|
|
}
|
|
}
|
|
|
|
write(code) {
|
|
if (code == null || code === '') {
|
|
return;
|
|
}
|
|
|
|
if (code instanceof Node) {
|
|
let node = code;
|
|
if (!node.writeCode) {
|
|
throw new Error('Node does not have a `writeCode` method: ' + JSON.stringify(node, null, 4));
|
|
}
|
|
node.writeCode(this);
|
|
} else if (isArray(code) || code instanceof Container) {
|
|
code.forEach(this.write, this);
|
|
return;
|
|
} else if (typeof code === 'string') {
|
|
this._code += code;
|
|
} else if (typeof code === 'boolean' || typeof code === 'number') {
|
|
this._code += code.toString();
|
|
} else {
|
|
throw new Error('Illegal argument: ' + JSON.stringify(code));
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
_write(code) {
|
|
this._code += code;
|
|
return this;
|
|
}
|
|
|
|
incIndent(count) {
|
|
if (count != null) {
|
|
for (let i=0; i<count; i++) {
|
|
this.currentIndent += ' ';
|
|
}
|
|
} else {
|
|
this.currentIndent += this._indentStr;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
decIndent(count) {
|
|
if (count == null) {
|
|
count = this._indentSize;
|
|
}
|
|
|
|
this.currentIndent = this.currentIndent.substring(
|
|
0,
|
|
this.currentIndent.length - count);
|
|
|
|
return this;
|
|
}
|
|
|
|
writeLineIndent() {
|
|
this._code += this.currentIndent;
|
|
return this;
|
|
}
|
|
|
|
writeIndent() {
|
|
this._code += this._indentStr;
|
|
return this;
|
|
}
|
|
|
|
isLiteralNode(node) {
|
|
return node instanceof Literal;
|
|
}
|
|
|
|
isIdentifierNode(node) {
|
|
return node instanceof Identifier;
|
|
}
|
|
|
|
writeLiteral(value) {
|
|
if (value === null) {
|
|
this.write('null');
|
|
} else if (value === undefined) {
|
|
this.write('undefined');
|
|
} else if (typeof value === 'string') {
|
|
this.write(JSON.stringify(value));
|
|
} else if (value === true) {
|
|
this.write('true');
|
|
} else if (value === false) {
|
|
this.write('false');
|
|
} else if (isArray(value)) {
|
|
if (value.length === 0) {
|
|
this.write('[]');
|
|
return;
|
|
}
|
|
|
|
this.write('[\n');
|
|
this.incIndent();
|
|
|
|
for (let i=0; i<value.length; i++) {
|
|
let v = value[i];
|
|
|
|
this.writeLineIndent();
|
|
|
|
if (v instanceof Node) {
|
|
this.write(v);
|
|
} else {
|
|
this.writeLiteral(v);
|
|
}
|
|
|
|
if (i < value.length - 1) {
|
|
this.write(',\n');
|
|
} else {
|
|
this.write('\n');
|
|
}
|
|
}
|
|
|
|
this.decIndent();
|
|
this.writeLineIndent();
|
|
this.write(']');
|
|
} else if (typeof value === 'number') {
|
|
this.write(value.toString());
|
|
} else if (value instanceof RegExp) {
|
|
this.write(value.toString());
|
|
} else if (typeof value === 'object') {
|
|
let keys = Object.keys(value);
|
|
if (keys.length === 0) {
|
|
this.write('{}');
|
|
return;
|
|
}
|
|
|
|
this.incIndent();
|
|
this.write('{\n');
|
|
this.incIndent();
|
|
|
|
for (let i=0; i<keys.length; i++) {
|
|
let k = keys[i];
|
|
let v = value[k];
|
|
|
|
this.writeLineIndent();
|
|
|
|
if (isValidJavaScriptVarName(k)) {
|
|
this.write(k + ': ');
|
|
} else {
|
|
this.write(JSON.stringify(k) + ': ');
|
|
}
|
|
|
|
if (v instanceof Node) {
|
|
this.write(v);
|
|
} else {
|
|
this.writeLiteral(v);
|
|
}
|
|
|
|
if (i < keys.length - 1) {
|
|
this.write(',\n');
|
|
} else {
|
|
this.write('\n');
|
|
}
|
|
}
|
|
|
|
this.decIndent();
|
|
this.writeLineIndent();
|
|
this.write('}');
|
|
this.decIndent();
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = CodeWriter; |