Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions src/message/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ export type MessageContextOptions<N = {}> = {
processor?: MessageProcessor
}

export const enum HelperNameMap {
LIST = 'list',
NAMED = 'named',
PLURAL_INDEX = 'pluralIndex',
PLURAL_RULE = 'pluralRule',
ORG_PLURAL_RULE = 'orgPluralRule',
MODIFIER = 'modifier',
MESSAGE = 'message',
TYPE = 'type',
INTERPOLATE = 'interpolate',
NORMALIZE = 'normalize'
}

export type MessageContext = {
list: (index: number) => unknown
named: (key: string) => unknown
Expand Down Expand Up @@ -166,15 +179,15 @@ export function createMessageContext<N = {}>(
: DEFAULT_INTERPOLATE

return {
list,
named,
pluralIndex,
pluralRule,
orgPluralRule,
modifier,
message,
type,
interpolate,
normalize
[HelperNameMap.LIST]: list,
[HelperNameMap.NAMED]: named,
[HelperNameMap.PLURAL_INDEX]: pluralIndex,
[HelperNameMap.PLURAL_RULE]: pluralRule,
[HelperNameMap.ORG_PLURAL_RULE]: orgPluralRule,
[HelperNameMap.MODIFIER]: modifier,
[HelperNameMap.MESSAGE]: message,
[HelperNameMap.TYPE]: type,
[HelperNameMap.INTERPOLATE]: interpolate,
[HelperNameMap.NORMALIZE]: normalize
}
}
50 changes: 39 additions & 11 deletions src/message/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
LiteralNode
} from './parser'
import { CodeGenOptions } from './options'

export const INTERPOLATE_CODE = `const interpolate = val => { return val == null ? "" : Array.isArray(val) || ((Object.prototype.toString.call(val) === "[object Object]") && val.toString === Object.prototype.toString) ? JSON.stringify(val, null, 2) : String(val) }`
import { HelperNameMap } from './context'
import { isString } from '../utils'

type CodeGenContext = {
source?: string
Expand All @@ -32,6 +32,7 @@ type CodeGenerator = Readonly<{
indent: () => void
deindent: (withoutNewLine?: boolean) => void
newline: () => void
helper: (key: string) => string
}>

function createCodeGenerator(source?: string): CodeGenerator {
Expand Down Expand Up @@ -67,34 +68,39 @@ function createCodeGenerator(source?: string): CodeGenerator {
_newline(_context.indentLevel)
}

const helper = (key: string): string => `_${key}`

return {
context,
push,
indent,
deindent,
newline
newline,
helper
}
}

function generateLinkedNode(generator: CodeGenerator, node: LinkedNode): void {
const { helper } = generator
if (node.modifier) {
generator.push('ctx.modifier(')
generator.push(`${helper(HelperNameMap.MODIFIER)}(`)
generateNode(generator, node.modifier)
generator.push(')(')
}
generator.push('ctx.message(')
generator.push(`${helper(HelperNameMap.MESSAGE)}(`)
generateNode(generator, node.key)
generator.push(')(ctx)')
if (node.modifier) {
generator.push(', ctx.type)')
generator.push(`, ${helper(HelperNameMap.TYPE)})`)
}
}

function generateMessageNode(
generator: CodeGenerator,
node: MessageNode
): void {
generator.push('ctx.normalize([')
const { helper } = generator
generator.push(`${helper(HelperNameMap.NORMALIZE)}([`)
generator.indent()
const length = node.items.length
for (let i = 0; i < length; i++) {
Expand All @@ -109,6 +115,7 @@ function generateMessageNode(
}

function generatePluralNode(generator: CodeGenerator, node: PluralNode): void {
const { helper } = generator
if (node.cases.length > 1) {
generator.push('[')
generator.indent()
Expand All @@ -122,7 +129,9 @@ function generatePluralNode(generator: CodeGenerator, node: PluralNode): void {
}
generator.deindent()
generator.push(
`][ctx.pluralRule(ctx.pluralIndex, ${length}, ctx.orgPluralRule)]`
`][${helper(HelperNameMap.PLURAL_RULE)}(${helper(
HelperNameMap.PLURAL_INDEX
)}, ${length}, ${helper(HelperNameMap.ORG_PLURAL_RULE)})]`
)
}
}
Expand All @@ -136,6 +145,7 @@ function generateResource(generator: CodeGenerator, node: ResourceNode): void {
}

function generateNode(generator: CodeGenerator, node: Node): void {
const { helper } = generator
switch (node.type) {
case NodeTypes.Resource:
generateResource(generator, node as ResourceNode)
Expand All @@ -156,11 +166,17 @@ function generateNode(generator: CodeGenerator, node: Node): void {
generator.push(JSON.stringify((node as LinkedKeyNode).value))
break
case NodeTypes.List:
generator.push(`ctx.interpolate(ctx.list(${(node as ListNode).index}))`)
generator.push(
`${helper(HelperNameMap.INTERPOLATE)}(${helper(HelperNameMap.LIST)}(${
(node as ListNode).index
}))`
)
break
case NodeTypes.Named:
generator.push(
`ctx.interpolate(ctx.named(${JSON.stringify((node as NamedNode).key)}))`
`${helper(HelperNameMap.INTERPOLATE)}(${helper(
HelperNameMap.NAMED
)}(${JSON.stringify((node as NamedNode).key)}))`
)
break
case NodeTypes.Literal:
Expand All @@ -181,12 +197,24 @@ export const generate = (
ast: ResourceNode,
options: CodeGenOptions = {} // eslint-disable-line
): string => {
const mode = isString(options.mode) ? options.mode : 'normal'
const helpers = ast.helpers || []
const generator = createCodeGenerator(ast.loc && ast.loc.source)
generator.push(`function __msg__ (ctx) {`)

generator.push(mode === 'normal' ? `function __msg__ (ctx) {` : `(ctx) => {`)
generator.indent()

if (helpers.length > 0) {
generator.push(
`const { ${helpers.map(s => `${s}: _${s}`).join(', ')} } = ctx`
)
generator.newline()
}

generator.push(`return `)
generateNode(generator, ast)
generator.deindent()
generator.push(`}`)

return generator.context().code
}
3 changes: 1 addition & 2 deletions src/message/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export type TransformOptions = {
}

export type CodeGenOptions = {
// TODO: other options
// onError?: CompileErrorHandler
mode?: 'normal' | 'arrow' // default normal
onError?: CompileErrorHandler
}

Expand Down
2 changes: 1 addition & 1 deletion src/message/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface Node {
export interface ResourceNode extends Node {
type: NodeTypes.Resource
body: MessageNode | PluralNode
needInterpolate?: boolean
helpers?: string[]
}

export interface PluralNode extends Node {
Expand Down
35 changes: 27 additions & 8 deletions src/message/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LinkedNode
} from './parser'
import { TransformOptions } from './options'
import { HelperNameMap } from './context'

// TODO: if we offer custom transform for uses, should be defined TransformOptions type to here
// ex.
Expand All @@ -15,24 +16,29 @@ import { TransformOptions } from './options'

type TransformContext = {
ast: ResourceNode
needInterpolate: boolean
helpers: Set<string>
}

type Transformer = Readonly<{
context: () => TransformContext
helper: (name: string) => string
}>

function createTransformer(
ast: ResourceNode /*, options: TransformOptions */
): Transformer {
const _context = {
ast,
needInterpolate: false
helpers: new Set()
} as TransformContext

const context = (): TransformContext => _context
const helper = (name: string): string => {
_context.helpers.add(name)
return name
}

return { context }
return { context, helper }
}

function traverseNodes(nodes: Node[], transformer: Transformer): void {
Expand All @@ -42,25 +48,35 @@ function traverseNodes(nodes: Node[], transformer: Transformer): void {
}

function traverseNode(node: Node, transformer: Transformer): void {
const context = transformer.context()

// TODO: if we need pre-hook of transform, should be implemeted to here

switch (node.type) {
case NodeTypes.Plural:
traverseNodes((node as PluralNode).cases, transformer)
transformer.helper(HelperNameMap.PLURAL_INDEX)
transformer.helper(HelperNameMap.PLURAL_RULE)
transformer.helper(HelperNameMap.ORG_PLURAL_RULE)
break
case NodeTypes.Message:
traverseNodes((node as MessageNode).items, transformer)
break
case NodeTypes.Linked:
const linked = node as LinkedNode
linked.modifier && traverseNode(linked.modifier, transformer)
if (linked.modifier) {
traverseNode(linked.modifier, transformer)
transformer.helper(HelperNameMap.MODIFIER)
transformer.helper(HelperNameMap.TYPE)
}
traverseNode(linked.key, transformer)
transformer.helper(HelperNameMap.MESSAGE)
break
case NodeTypes.List:
transformer.helper(HelperNameMap.INTERPOLATE)
transformer.helper(HelperNameMap.LIST)
break
case NodeTypes.Named:
context.needInterpolate = true
transformer.helper(HelperNameMap.INTERPOLATE)
transformer.helper(HelperNameMap.NAMED)
break
default:
// TODO:
Expand All @@ -76,9 +92,12 @@ export function transform(
options: TransformOptions = {} // eslint-disable-line
): void {
const transformer = createTransformer(ast)
transformer.helper(HelperNameMap.NORMALIZE)

// traverse
ast.body && traverseNode(ast.body, transformer)

// set meta information
const context = transformer.context()
ast.needInterpolate = context.needInterpolate
ast.helpers = [...context.helpers]
}
32 changes: 18 additions & 14 deletions test/message/__snapshots__/compiler.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,33 @@

exports[`@.caml:{'no apples'} | {0} apple | {n} apples: code 1`] = `
"function __msg__ (ctx) {
const { normalize: _normalize, modifier: _modifier, type: _type, message: _message, interpolate: _interpolate, list: _list, named: _named, pluralIndex: _pluralIndex, pluralRule: _pluralRule, orgPluralRule: _orgPluralRule } = ctx
return [
ctx.normalize([
ctx.modifier(\\"caml\\")(ctx.message(\\"no apples\\")(ctx), ctx.type)
]), ctx.normalize([
ctx.interpolate(ctx.list(0)), \\" apple\\"
]), ctx.normalize([
ctx.interpolate(ctx.named(\\"n\\")), \\" apples\\"
_normalize([
_modifier(\\"caml\\")(_message(\\"no apples\\")(ctx), _type)
]), _normalize([
_interpolate(_list(0)), \\" apple\\"
]), _normalize([
_interpolate(_named(\\"n\\")), \\" apples\\"
])
][ctx.pluralRule(ctx.pluralIndex, 3, ctx.orgPluralRule)]
][_pluralRule(_pluralIndex, 3, _orgPluralRule)]
}"
`;

exports[`edge cases | | | : code 1`] = `
"function __msg__ (ctx) {
const { normalize: _normalize, pluralIndex: _pluralIndex, pluralRule: _pluralRule, orgPluralRule: _orgPluralRule } = ctx
return [
ctx.normalize([
_normalize([

]), ctx.normalize([
]), _normalize([

]), ctx.normalize([
]), _normalize([

]), ctx.normalize([
]), _normalize([

])
][ctx.pluralRule(ctx.pluralIndex, 4, ctx.orgPluralRule)]
][_pluralRule(_pluralIndex, 4, _orgPluralRule)]
}"
`;

Expand All @@ -52,15 +54,17 @@ Object {

exports[`warnHtmlMessage default: code 1`] = `
"function __msg__ (ctx) {
return ctx.normalize([
const { normalize: _normalize } = ctx
return _normalize([
\\"<p>hello</p>\\"
])
}"
`;

exports[`warnHtmlMessage false: code 1`] = `
"function __msg__ (ctx) {
return ctx.normalize([
const { normalize: _normalize } = ctx
return _normalize([
\\"<p>hello</p>\\"
])
}"
Expand Down
Loading