diff --git a/src/Compiler/Components.js b/src/Compiler/Components.js deleted file mode 100644 index 0df178b..0000000 --- a/src/Compiler/Components.js +++ /dev/null @@ -1,42 +0,0 @@ -import '../AST' -import '../Errors/StoneCompilerError' - -export function compileComponent(context, args) { - args = context.parseArguments(args) - - let code = 'output += (function() {' - code += `\nconst __componentView = ${AST.stringify(args[0])};` - - if(args.length > 1) { - code += `\nconst __componentContext = ${AST.stringify(args[1])};` - } else { - code += '\nconst __componentContext = { };' - } - - code += '\nlet output = \'\';' - - return code -} - -export function compileEndcomponent() { - const context = 'Object.assign({ slot: new HtmlString(output) }, __componentContext)' - return `return _.$stone.include(_, { }, __templatePathname, __componentView, ${context});\n})()` -} - -export function compileSlot(context, args) { - args = context.parseArguments(args) - - if(args.length === 1) { - return `__componentContext[${AST.stringify(args[0])}] = (function() {\nlet output = '';` - } - - if(args.length !== 2) { - throw new StoneCompilerError(context, 'Invalid slot') - } - - return `__componentContext[${AST.stringify(args[0])}] = escape(${AST.stringify(args[1])});` -} - -export function compileEndslot() { - return 'return new HtmlString(output); })()' -} diff --git a/src/Stone/Parsers/Components.js b/src/Stone/Parsers/Components.js new file mode 100644 index 0000000..f2a0b69 --- /dev/null +++ b/src/Stone/Parsers/Components.js @@ -0,0 +1,79 @@ +export function parseComponentDirective(node, args) { + args = this._flattenArgs(args) + + if(args.length === 0) { + this.raise(this.start, '`@component` must contain at least 1 argument') + } + + node.view = args.shift() + + if(args.length > 1) { + this.raise(this.start, '`@component` cannot contain more than 2 arguments') + } else if(args.length === 1) { + node.context = args.pop() + } + + (this._currentComponent = (this._currentComponent || [ ])).push(node) + + const output = this.startNode() + output.params = args + output.body = this.parseUntilEndDirective('endcomponent') + node.output = this.finishNode(output, 'StoneOutputBlock') + + return this.finishNode(node, 'StoneComponent') +} + +/** + * Ends the current component and returns output + * @return {string} Output from the component + */ +export function parseEndcomponentDirective(node) { + if(!this._currentComponent || this._currentComponent.length === 0) { + this.raise(this.start, '`@endcomponent` outside of `@component`') + } + + this._currentComponent.pop() + + return this.finishNode(node, 'Directive') +} + +export function parseSlotDirective(node, args) { + args = this._flattenArgs(args) + + if(args.length === 0) { + this.raise(this.start, '`@slot` must contain at least 1 argument') + } + + node.id = args.shift() + + if(args.length > 1) { + this.raise(this.start, '`@slot` cannot contain more than 2 arguments') + } else if(args.length === 1) { + node.output = args.pop() + node.inline = true + this.next() + } else { + (this._currentSlot = (this._currentSlot || [ ])).push(node) + + const output = this.startNode() + output.params = args + output.body = this.parseUntilEndDirective('endslot') + node.output = this.finishNode(output, 'StoneOutputBlock') + } + + return this.finishNode(node, 'StoneSlot') +} + +/** + * Ends the current slot and returns output + * @return {string} Output from the slot + */ +export function parseEndslotDirective(node) { + if(!this._currentSlot || this._currentSlot.length === 0) { + this.raise(this.start, '`@endslot` outside of `@slot`') + } + + this._currentSlot.pop() + + return this.finishNode(node, 'Directive') +} diff --git a/src/Stone/Parsers/index.js b/src/Stone/Parsers/index.js index d36ba22..59e14b8 100644 --- a/src/Stone/Parsers/index.js +++ b/src/Stone/Parsers/index.js @@ -1,4 +1,5 @@ export const Parsers = { + ...require('./Components'), ...require('./Conditionals'), ...require('./Includes'), ...require('./Layouts'), diff --git a/src/Stone/Types/StoneComponent.js b/src/Stone/Types/StoneComponent.js new file mode 100644 index 0000000..8bd1405 --- /dev/null +++ b/src/Stone/Types/StoneComponent.js @@ -0,0 +1,56 @@ +import { make } from '../Support/MakeNode' + +export function generate(node, state) { + node.output.assignments = node.output.assignments || [ ] + + node.output.assignments.push({ + kind: 'const', + left: make.identifier('__componentView'), + right: node.view + }) + + node.output.assignments.push({ + kind: 'const', + left: make.identifier('__componentContext'), + right: !node.context.isNil ? node.context : make.object() + }) + + node.output.return = { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { + type: 'MemberExpression', + object: make.identifier('_'), + property: make.identifier('$stone'), + }, + property: make.identifier('include'), + }, + arguments: [ + make.identifier('_'), + make.null(), + make.identifier('_templatePathname'), + make.identifier('__componentView'), + make.object([ + make.property('slot', make.new('HtmlString', 'output')), + make.spread('__componentContext') + ]) + ] + } + + state.write('output += (') + this[node.output.type](node.output, state) + state.write(')();') +} + +export function walk(node, st, c) { + +} + +export function scope(node, scope) { + node.scope = new Set(scope) + node.scope.add('__componentView') + node.scope.add('__componentContext') + + this._scope(node.output, node.scope) +} diff --git a/src/Stone/Types/StoneSlot.js b/src/Stone/Types/StoneSlot.js new file mode 100644 index 0000000..a6dca2c --- /dev/null +++ b/src/Stone/Types/StoneSlot.js @@ -0,0 +1,29 @@ +export function generate(node, state) { + state.write('__componentContext[') + this[node.id.type](node.id, state) + state.write('] = ') + + if(node.inline) { + this.StoneOutputExpression({ safe: true, value: node.output }, state) + } else { + state.write('(') + this[node.output.type](node.output, state) + state.write(')()') + } + + state.write(';') +} + +export function walk(node, st, c) { + c(node.id, st, 'Pattern') + + if(node.inline) { + return + } + + c(node.output, st, 'Expression') +} + +export function scope(node, scope) { + this._scope(node.output, scope) +} diff --git a/src/Stone/Types/index.js b/src/Stone/Types/index.js index 4cbf872..e176bba 100644 --- a/src/Stone/Types/index.js +++ b/src/Stone/Types/index.js @@ -1,4 +1,5 @@ export const Types = { + StoneComponent: require('./StoneComponent'), StoneDump: require('./StoneDump'), StoneEach: require('./StoneEach'), StoneEmptyExpression: require('./StoneEmptyExpression'), @@ -11,6 +12,7 @@ export const Types = { StoneOutputBlock: require('./StoneOutputBlock'), StoneOutputExpression: require('./StoneOutputExpression'), StoneSection: require('./StoneSection'), + StoneSlot: require('./StoneSlot'), StoneSuper: require('./StoneSuper'), StoneTemplate: require('./StoneTemplate'), StoneYield: require('./StoneYield'),