UNPKG

@stackpress/idea-parser

Version:

Parses ideas to AST and readable JSON.

214 lines (213 loc) 8.13 kB
import Exception from './Exception.js'; export default class Compiler { static array(token, references = false) { return token.elements.map(element => this.data(element, references)); } static data(token, references = false) { if (token.type === 'ObjectExpression') { return this.object(token, references); } else if (token.type === 'ArrayExpression') { return this.array(token, references); } else if (token.type === 'Literal') { return this.literal(token); } else if (token.type === 'Identifier') { return this.identifier(token, references); } throw Exception.for('Invalid data token type'); } static enum(token) { if (token.kind !== 'enum') { throw Exception.for('Invalid Enum'); } const name = token.declarations?.[0].id?.name; const options = {}; token.declarations[0].init.properties.forEach(property => { options[property.key.name] = property.value.value; }); return [name, options]; } static final(token) { const schema = this.schema(token, true); delete schema.use; delete schema.prop; return schema; } static identifier(token, references = false) { if (references && token.name in references) { return references[token.name]; } else if (references === false) { return '${' + token.name + '}'; } throw Exception.for(`Unknown reference ${token.name}`); } static literal(token) { return token.value; } static model(token, references = false) { const name = token.declarations[0].id?.name; const mutable = token.mutable !== false; const value = {}; token.declarations[0].init.properties.forEach(property => { value[property.key.name] = this.data(property.value, references); }); if (typeof value.columns !== 'object') { throw Exception.for('Expecting a columns property'); } const columns = []; for (const name in value.columns) { const column = value.columns[name]; column.name = name; if (typeof column.type === 'string') { column.required = !column.type.endsWith('?'); column.type = column.type.replace(/\?$/, ''); column.multiple = column.type.endsWith('[]'); column.type = column.type.replace(/\[\]$/, ''); } columns.push({ type: column.type, name: column.name, required: column.required, multiple: column.multiple, attributes: column.attributes, ...column }); } value.columns = columns; return [name, { name, mutable, ...value }]; } static object(token, references = false) { return Object.fromEntries(token.properties.map(property => [ property.key.name, this.data(property.value, references) ])); } static plugin(token) { if (token.kind !== 'plugin') { throw Exception.for('Invalid Plugin'); } const name = token.declarations?.[0].id?.name; const value = {}; token.declarations[0].init.properties.forEach(property => { value[property.key.name] = this.data(property.value); }); return [name, value]; } static prop(token, references = false) { if (token.kind !== 'prop') { throw Exception.for('Invalid Prop'); } const name = token.declarations[0].id.name; const value = {}; token.declarations[0].init.properties.forEach(property => { value[property.key.name] = this.data(property.value, references); }); return [name, value]; } static schema(token, finalize = false) { if (token.kind !== 'schema') { throw Exception.for('Invalid Schema'); } const schema = {}; const references = {}; const uses = token.body.filter(token => token.type === 'ImportDeclaration'); uses.forEach(use => { schema.use = schema.use || []; schema.use.push(this.use(use)); }); const declarations = token.body; declarations.filter(declaration => declaration.kind).forEach(declaration => { if (declaration.kind === 'enum') { schema.enum = schema.enum || {}; const [key, value] = this.enum(declaration); schema.enum[key] = value; if (references[key]) { throw Exception.for('Duplicate %s', key); } references[key] = value; } else if (declaration.kind === 'prop') { schema.prop = schema.prop || {}; const [key, value] = this.prop(declaration, finalize ? references : false); schema.prop[key] = value; if (references[key]) { throw Exception.for('Duplicate %s', key); } references[key] = value; } else if (declaration.kind === 'type') { schema.type = schema.type || {}; const [key, value] = this.type(declaration, finalize ? references : false); schema.type[key] = value; if (references[key]) { throw Exception.for('Duplicate %s', key); } references[key] = value; } else if (declaration.kind === 'model') { schema.model = schema.model || {}; const [key, value] = this.model(declaration, finalize ? references : false); schema.model[key] = value; if (references[key]) { throw Exception.for('Duplicate %s', key); } references[key] = value; } else if (declaration.kind === 'plugin') { schema.plugin = schema.plugin || {}; const [key, value] = this.plugin(declaration); schema.plugin[key] = value; if (references[key]) { throw Exception.for('Duplicate %s', key); } references[key] = value; } }); return schema; } static type(token, references = false) { if (token.kind !== 'type') { throw Exception.for('Invalid Type'); } const name = token.declarations[0].id.name; const mutable = token.mutable !== false; const value = {}; token.declarations[0].init.properties.forEach(property => { value[property.key.name] = this.data(property.value, references); }); if (typeof value.columns !== 'object') { throw Exception.for('Expecting a columns property'); } const columns = []; for (const name in value.columns) { const column = value.columns[name]; column.name = name; if (typeof column.type === 'string') { column.required = !column.type.endsWith('?'); column.type = column.type.replace(/\?$/, ''); column.multiple = column.type.endsWith('[]'); column.type = column.type.replace(/\[\]$/, ''); } columns.push({ type: column.type, name: column.name, required: column.required, multiple: column.multiple, attributes: column.attributes, ...column }); } value.columns = columns; return [name, { name, mutable, ...value }]; } static use(token) { if (token.type !== 'ImportDeclaration') { throw Exception.for('Invalid Import'); } return token.source.value; } } ;