diff --git a/fixtures/set-tag-identifier/compiled.js b/fixtures/set-tag-identifier/compiled.js new file mode 100644 index 0000000..7bdde35 --- /dev/null +++ b/fixtures/set-tag-identifier/compiled.js @@ -0,0 +1,7 @@ +(function (template, ctx) { + let out = '' + ctx.set('username', ctx.resolve('username').split('')[0]) + out += `${ctx.escape(ctx.resolve('username'))}` + out += '\n' + return out +})(template, ctx) \ No newline at end of file diff --git a/fixtures/set-tag-identifier/index.edge b/fixtures/set-tag-identifier/index.edge new file mode 100644 index 0000000..3e43542 --- /dev/null +++ b/fixtures/set-tag-identifier/index.edge @@ -0,0 +1,2 @@ +@set('username', username.split('')[0]) +{{ username }} \ No newline at end of file diff --git a/fixtures/set-tag-identifier/index.json b/fixtures/set-tag-identifier/index.json new file mode 100644 index 0000000..d5f5540 --- /dev/null +++ b/fixtures/set-tag-identifier/index.json @@ -0,0 +1,3 @@ +{ + "username": "virk" +} \ No newline at end of file diff --git a/fixtures/set-tag-identifier/index.txt b/fixtures/set-tag-identifier/index.txt new file mode 100644 index 0000000..bb79ec2 --- /dev/null +++ b/fixtures/set-tag-identifier/index.txt @@ -0,0 +1 @@ +v \ No newline at end of file diff --git a/fixtures/set-tag/compiled.js b/fixtures/set-tag/compiled.js new file mode 100644 index 0000000..a789af3 --- /dev/null +++ b/fixtures/set-tag/compiled.js @@ -0,0 +1,7 @@ +(function (template, ctx) { + let out = '' + ctx.set('username', 'nikk') + out += `${ctx.escape(ctx.resolve('username'))}` + out += '\n' + return out +})(template, ctx) \ No newline at end of file diff --git a/fixtures/set-tag/index.edge b/fixtures/set-tag/index.edge new file mode 100644 index 0000000..43f8ead --- /dev/null +++ b/fixtures/set-tag/index.edge @@ -0,0 +1,2 @@ +@set('username', 'nikk') +{{ username }} \ No newline at end of file diff --git a/fixtures/set-tag/index.json b/fixtures/set-tag/index.json new file mode 100644 index 0000000..d5f5540 --- /dev/null +++ b/fixtures/set-tag/index.json @@ -0,0 +1,3 @@ +{ + "username": "virk" +} \ No newline at end of file diff --git a/fixtures/set-tag/index.txt b/fixtures/set-tag/index.txt new file mode 100644 index 0000000..c12829d --- /dev/null +++ b/fixtures/set-tag/index.txt @@ -0,0 +1 @@ +nikk \ No newline at end of file diff --git a/src/Tags/Component.ts b/src/Tags/Component.ts index 86807cd..d92f6c3 100644 --- a/src/Tags/Component.ts +++ b/src/Tags/Component.ts @@ -10,8 +10,7 @@ import { Parser } from 'edge-parser' import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' import { IBlockNode } from 'edge-lexer/build/src/Contracts' -import { UnAllowedExpressionException, TooManyArgumentsException } from '../Exceptions' -import { parseSequenceExpression, ObjectifyString } from '../utils' +import { parseSequenceExpression, ObjectifyString, parseAsKeyValuePair } from '../utils' export class ComponentTag { public static block = true @@ -19,59 +18,6 @@ export class ComponentTag { public static selfclosed = true public static tagName = 'component' - /** - * Returns the slot name and the slot prop name. - */ - private _getNameAndPropsName (statement: IBlockNode, parser: Parser): [string, string | null] { - const ast = parser.generateAst(statement.properties.jsArg, statement.lineno) - const expression = ast.body[0].expression - - /** - * Return without counting props, value is a literal - */ - if (expression.type === 'Literal') { - return [expression.raw, null] - } - - /** - * Raise error when not a sequence expression - */ - if (expression.type !== 'SequenceExpression') { - throw UnAllowedExpressionException.invoke('slot', expression.type, expression.loc.start.line) - } - - /** - * Raise error when more than 2 arguments are passed to the slot - * expression - */ - if (expression.expressions.length > 2) { - throw TooManyArgumentsException.invoke('slot', 2, expression.loc.start.line) - } - - /** - * Raise error when first expression or sequence expressions is not a literal. - * Since slot name must be a string - */ - if (expression.expressions[0].type !== 'Literal') { - throw UnAllowedExpressionException - .invoke('slot', expression.expressions[0].type, expression.expressions[0].loc.start.line) - } - - /** - * Raise error when 2nd expression of the sequence expressions is not an indentifier. - * Since slot prop has to be an identifier - */ - if (expression.expressions[1].type !== 'Identifier') { - throw UnAllowedExpressionException - .invoke('slot', expression.expressions[1].type, expression.expressions[1].loc.end.line) - } - - /** - * Finally return the name and prop name for the slot - */ - return [expression.expressions[0].raw, expression.expressions[1].name] - } - /** * Compiles else block node to Javascript else statement */ @@ -87,7 +33,8 @@ export class ComponentTag { let slotProps: string | null = null if (child.type === 'block' && (child as IBlockNode).properties.name === 'slot') { - const parsed = this._getNameAndPropsName((child as IBlockNode), parser) + const statement = parser.generateAst((child as IBlockNode).properties.jsArg, child.lineno) + const parsed = parseAsKeyValuePair(statement.body[0].expression, parser, ['Identifier']) slotName = parsed[0] slotProps = parsed[1] } diff --git a/src/Tags/Set.ts b/src/Tags/Set.ts new file mode 100644 index 0000000..faec298 --- /dev/null +++ b/src/Tags/Set.ts @@ -0,0 +1,29 @@ +/* +* edge +* +* (c) Harminder Virk +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import { Parser } from 'edge-parser' +import { EdgeBuffer } from 'edge-parser/build/src/EdgeBuffer' +import { IBlockNode } from 'edge-lexer/build/src/Contracts' +import { parseAsKeyValuePair } from '../utils' + +export class SetTag { + public static block = false + public static seekable = true + public static selfclosed = false + public static tagName = 'set' + + /** + * Compiles else block node to Javascript else statement + */ + public compile (parser: Parser, buffer: EdgeBuffer, token: IBlockNode) { + const ast = parser.parseJsArg(token.properties.jsArg, token.lineno) + const [key, value] = parseAsKeyValuePair(ast, parser, []) + buffer.writeStatement(`ctx.set(${key}, ${value})`) + } +} diff --git a/src/Tags/index.ts b/src/Tags/index.ts index c89921a..6830810 100644 --- a/src/Tags/index.ts +++ b/src/Tags/index.ts @@ -15,3 +15,4 @@ export { EachTag as each } from './Each' export { ComponentTag as component } from './Component' export { SlotTag as slot } from './Slot' export { DebuggerTag as debugger } from './Debugger' +export { SetTag as set } from './Set' diff --git a/src/utils/index.ts b/src/utils/index.ts index 1868b37..f415206 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,7 +7,7 @@ * file that was distributed with this source code. */ -import { UnAllowedExpressionException } from '../Exceptions' +import { UnAllowedExpressionException, TooManyArgumentsException } from '../Exceptions' import { Parser } from 'edge-parser' import { sep } from 'path' @@ -38,7 +38,7 @@ export class ObjectifyString { */ export function allowExpressions (tag: string, expression: any, expressions: string[]) { if (expressions.indexOf(expression.type) === -1) { - throw UnAllowedExpressionException.invoke('if', expression.type, expression.loc.start.line) + throw UnAllowedExpressionException.invoke(tag, expression.type, expression.loc.start.line) } } @@ -48,7 +48,7 @@ export function allowExpressions (tag: string, expression: any, expressions: str */ export function disAllowExpressions (tag: string, expression: any, expressions: string[]) { if (expressions.indexOf(expression.type) > -1) { - throw UnAllowedExpressionException.invoke('if', expression.type, expression.loc.start.line) + throw UnAllowedExpressionException.invoke(tag, expression.type, expression.loc.start.line) } } @@ -98,6 +98,54 @@ export function parseSequenceExpression (expression: any, parser: Parser): [stri return [name, `{}`] } +/** + * Parses an expression as a key/value pair and has following constraints. + * + * 1. Top level expression must be `Literal` or `SequenceExpression`. + * 2. If `SequenceExpression`, then first child of expression must be `Literal` + * 3. Length of `SequenceExpression` childs must be 2 at max. + * + * Optionally, you can enforce (3rd argument) that value in the key/value pair must be one + * of the given expressions. + * + * ``` + * // Following are the valid expressions + * ('foo', 'bar') + * ('foo') + * ('foo', bar) + * ('foo', { bar: true }) + * ``` + */ +export function parseAsKeyValuePair (expression: any, parser: Parser, valueExpressions: string[]): [string, null | string] { + allowExpressions('slot', expression, ['Literal', 'SequenceExpression']) + + /** + * Return without counting props, value is a literal + */ + if (expression.type === 'Literal') { + return [expression.raw, null] + } + + /** + * Raise error when more than 2 arguments are passed to the slot + * expression + */ + if (expression.expressions.length > 2) { + throw TooManyArgumentsException.invoke('slot', 2, expression.loc.start.line) + } + + allowExpressions('slot', expression.expressions[0], ['Literal']) + + if (valueExpressions.length) { + allowExpressions('slot', expression.expressions[1], valueExpressions) + } + + /** + * Finally return the name and prop name for the slot + */ + return [expression.expressions[0].raw, parser.statementToString(expression.expressions[1])] +} + /** * Extracts the disk name and the template name from the template * path expression. diff --git a/test/tags.spec.ts b/test/tags.spec.ts index be28aa5..7f0ff31 100644 --- a/test/tags.spec.ts +++ b/test/tags.spec.ts @@ -105,7 +105,7 @@ test.group('Include', (group) => { compiler.compile('foo', true) } catch (error) { assert.equal(error.line, 1) - assert.equal(error.message, 'E_UNALLOWED_EXPRESSION: SequenceExpression is not allowed for if tag\n> More details: https://err.sh/poppinss/edge-errors/E_UNALLOWED_EXPRESSION') + assert.equal(error.message, 'E_UNALLOWED_EXPRESSION: SequenceExpression is not allowed for include tag\n> More details: https://err.sh/poppinss/edge-errors/E_UNALLOWED_EXPRESSION') } }) }) @@ -189,7 +189,7 @@ test.group('Component', (group) => { try { compiler.compile('foo', true) } catch (error) { - assert.equal(error.line, 5) + assert.equal(error.line, 3) assert.equal(error.message, 'E_UNALLOWED_EXPRESSION: Literal is not allowed for slot tag\n> More details: https://err.sh/poppinss/edge-errors/E_UNALLOWED_EXPRESSION') } })