Skip to content

Commit 75a2430

Browse files
authored
feat: generator mode (#31)
1 parent 35f59cb commit 75a2430

File tree

10 files changed

+263
-128
lines changed

10 files changed

+263
-128
lines changed

src/message/context.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ export type MessageContextOptions<N = {}> = {
4141
processor?: MessageProcessor
4242
}
4343

44+
export const enum HelperNameMap {
45+
LIST = 'list',
46+
NAMED = 'named',
47+
PLURAL_INDEX = 'pluralIndex',
48+
PLURAL_RULE = 'pluralRule',
49+
ORG_PLURAL_RULE = 'orgPluralRule',
50+
MODIFIER = 'modifier',
51+
MESSAGE = 'message',
52+
TYPE = 'type',
53+
INTERPOLATE = 'interpolate',
54+
NORMALIZE = 'normalize'
55+
}
56+
4457
export type MessageContext = {
4558
list: (index: number) => unknown
4659
named: (key: string) => unknown
@@ -166,15 +179,15 @@ export function createMessageContext<N = {}>(
166179
: DEFAULT_INTERPOLATE
167180

168181
return {
169-
list,
170-
named,
171-
pluralIndex,
172-
pluralRule,
173-
orgPluralRule,
174-
modifier,
175-
message,
176-
type,
177-
interpolate,
178-
normalize
182+
[HelperNameMap.LIST]: list,
183+
[HelperNameMap.NAMED]: named,
184+
[HelperNameMap.PLURAL_INDEX]: pluralIndex,
185+
[HelperNameMap.PLURAL_RULE]: pluralRule,
186+
[HelperNameMap.ORG_PLURAL_RULE]: orgPluralRule,
187+
[HelperNameMap.MODIFIER]: modifier,
188+
[HelperNameMap.MESSAGE]: message,
189+
[HelperNameMap.TYPE]: type,
190+
[HelperNameMap.INTERPOLATE]: interpolate,
191+
[HelperNameMap.NORMALIZE]: normalize
179192
}
180193
}

src/message/generator.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
LiteralNode
1414
} from './parser'
1515
import { CodeGenOptions } from './options'
16-
17-
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) }`
16+
import { HelperNameMap } from './context'
17+
import { isString } from '../utils'
1818

1919
type CodeGenContext = {
2020
source?: string
@@ -32,6 +32,7 @@ type CodeGenerator = Readonly<{
3232
indent: () => void
3333
deindent: (withoutNewLine?: boolean) => void
3434
newline: () => void
35+
helper: (key: string) => string
3536
}>
3637

3738
function createCodeGenerator(source?: string): CodeGenerator {
@@ -67,34 +68,39 @@ function createCodeGenerator(source?: string): CodeGenerator {
6768
_newline(_context.indentLevel)
6869
}
6970

71+
const helper = (key: string): string => `_${key}`
72+
7073
return {
7174
context,
7275
push,
7376
indent,
7477
deindent,
75-
newline
78+
newline,
79+
helper
7680
}
7781
}
7882

7983
function generateLinkedNode(generator: CodeGenerator, node: LinkedNode): void {
84+
const { helper } = generator
8085
if (node.modifier) {
81-
generator.push('ctx.modifier(')
86+
generator.push(`${helper(HelperNameMap.MODIFIER)}(`)
8287
generateNode(generator, node.modifier)
8388
generator.push(')(')
8489
}
85-
generator.push('ctx.message(')
90+
generator.push(`${helper(HelperNameMap.MESSAGE)}(`)
8691
generateNode(generator, node.key)
8792
generator.push(')(ctx)')
8893
if (node.modifier) {
89-
generator.push(', ctx.type)')
94+
generator.push(`, ${helper(HelperNameMap.TYPE)})`)
9095
}
9196
}
9297

9398
function generateMessageNode(
9499
generator: CodeGenerator,
95100
node: MessageNode
96101
): void {
97-
generator.push('ctx.normalize([')
102+
const { helper } = generator
103+
generator.push(`${helper(HelperNameMap.NORMALIZE)}([`)
98104
generator.indent()
99105
const length = node.items.length
100106
for (let i = 0; i < length; i++) {
@@ -109,6 +115,7 @@ function generateMessageNode(
109115
}
110116

111117
function generatePluralNode(generator: CodeGenerator, node: PluralNode): void {
118+
const { helper } = generator
112119
if (node.cases.length > 1) {
113120
generator.push('[')
114121
generator.indent()
@@ -122,7 +129,9 @@ function generatePluralNode(generator: CodeGenerator, node: PluralNode): void {
122129
}
123130
generator.deindent()
124131
generator.push(
125-
`][ctx.pluralRule(ctx.pluralIndex, ${length}, ctx.orgPluralRule)]`
132+
`][${helper(HelperNameMap.PLURAL_RULE)}(${helper(
133+
HelperNameMap.PLURAL_INDEX
134+
)}, ${length}, ${helper(HelperNameMap.ORG_PLURAL_RULE)})]`
126135
)
127136
}
128137
}
@@ -136,6 +145,7 @@ function generateResource(generator: CodeGenerator, node: ResourceNode): void {
136145
}
137146

138147
function generateNode(generator: CodeGenerator, node: Node): void {
148+
const { helper } = generator
139149
switch (node.type) {
140150
case NodeTypes.Resource:
141151
generateResource(generator, node as ResourceNode)
@@ -156,11 +166,17 @@ function generateNode(generator: CodeGenerator, node: Node): void {
156166
generator.push(JSON.stringify((node as LinkedKeyNode).value))
157167
break
158168
case NodeTypes.List:
159-
generator.push(`ctx.interpolate(ctx.list(${(node as ListNode).index}))`)
169+
generator.push(
170+
`${helper(HelperNameMap.INTERPOLATE)}(${helper(HelperNameMap.LIST)}(${
171+
(node as ListNode).index
172+
}))`
173+
)
160174
break
161175
case NodeTypes.Named:
162176
generator.push(
163-
`ctx.interpolate(ctx.named(${JSON.stringify((node as NamedNode).key)}))`
177+
`${helper(HelperNameMap.INTERPOLATE)}(${helper(
178+
HelperNameMap.NAMED
179+
)}(${JSON.stringify((node as NamedNode).key)}))`
164180
)
165181
break
166182
case NodeTypes.Literal:
@@ -181,12 +197,24 @@ export const generate = (
181197
ast: ResourceNode,
182198
options: CodeGenOptions = {} // eslint-disable-line
183199
): string => {
200+
const mode = isString(options.mode) ? options.mode : 'normal'
201+
const helpers = ast.helpers || []
184202
const generator = createCodeGenerator(ast.loc && ast.loc.source)
185-
generator.push(`function __msg__ (ctx) {`)
203+
204+
generator.push(mode === 'normal' ? `function __msg__ (ctx) {` : `(ctx) => {`)
186205
generator.indent()
206+
207+
if (helpers.length > 0) {
208+
generator.push(
209+
`const { ${helpers.map(s => `${s}: _${s}`).join(', ')} } = ctx`
210+
)
211+
generator.newline()
212+
}
213+
187214
generator.push(`return `)
188215
generateNode(generator, ast)
189216
generator.deindent()
190217
generator.push(`}`)
218+
191219
return generator.context().code
192220
}

src/message/options.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ export type TransformOptions = {
2828
}
2929

3030
export type CodeGenOptions = {
31-
// TODO: other options
32-
// onError?: CompileErrorHandler
31+
mode?: 'normal' | 'arrow' // default normal
3332
onError?: CompileErrorHandler
3433
}
3534

src/message/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface Node {
2929
export interface ResourceNode extends Node {
3030
type: NodeTypes.Resource
3131
body: MessageNode | PluralNode
32-
needInterpolate?: boolean
32+
helpers?: string[]
3333
}
3434

3535
export interface PluralNode extends Node {

src/message/transformer.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
LinkedNode
88
} from './parser'
99
import { TransformOptions } from './options'
10+
import { HelperNameMap } from './context'
1011

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

1617
type TransformContext = {
1718
ast: ResourceNode
18-
needInterpolate: boolean
19+
helpers: Set<string>
1920
}
2021

2122
type Transformer = Readonly<{
2223
context: () => TransformContext
24+
helper: (name: string) => string
2325
}>
2426

2527
function createTransformer(
2628
ast: ResourceNode /*, options: TransformOptions */
2729
): Transformer {
2830
const _context = {
2931
ast,
30-
needInterpolate: false
32+
helpers: new Set()
3133
} as TransformContext
3234

3335
const context = (): TransformContext => _context
36+
const helper = (name: string): string => {
37+
_context.helpers.add(name)
38+
return name
39+
}
3440

35-
return { context }
41+
return { context, helper }
3642
}
3743

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

4450
function traverseNode(node: Node, transformer: Transformer): void {
45-
const context = transformer.context()
46-
4751
// TODO: if we need pre-hook of transform, should be implemeted to here
4852

4953
switch (node.type) {
5054
case NodeTypes.Plural:
5155
traverseNodes((node as PluralNode).cases, transformer)
56+
transformer.helper(HelperNameMap.PLURAL_INDEX)
57+
transformer.helper(HelperNameMap.PLURAL_RULE)
58+
transformer.helper(HelperNameMap.ORG_PLURAL_RULE)
5259
break
5360
case NodeTypes.Message:
5461
traverseNodes((node as MessageNode).items, transformer)
5562
break
5663
case NodeTypes.Linked:
5764
const linked = node as LinkedNode
58-
linked.modifier && traverseNode(linked.modifier, transformer)
65+
if (linked.modifier) {
66+
traverseNode(linked.modifier, transformer)
67+
transformer.helper(HelperNameMap.MODIFIER)
68+
transformer.helper(HelperNameMap.TYPE)
69+
}
5970
traverseNode(linked.key, transformer)
71+
transformer.helper(HelperNameMap.MESSAGE)
6072
break
6173
case NodeTypes.List:
74+
transformer.helper(HelperNameMap.INTERPOLATE)
75+
transformer.helper(HelperNameMap.LIST)
76+
break
6277
case NodeTypes.Named:
63-
context.needInterpolate = true
78+
transformer.helper(HelperNameMap.INTERPOLATE)
79+
transformer.helper(HelperNameMap.NAMED)
6480
break
6581
default:
6682
// TODO:
@@ -76,9 +92,12 @@ export function transform(
7692
options: TransformOptions = {} // eslint-disable-line
7793
): void {
7894
const transformer = createTransformer(ast)
95+
transformer.helper(HelperNameMap.NORMALIZE)
96+
7997
// traverse
8098
ast.body && traverseNode(ast.body, transformer)
99+
81100
// set meta information
82101
const context = transformer.context()
83-
ast.needInterpolate = context.needInterpolate
102+
ast.helpers = [...context.helpers]
84103
}

test/message/__snapshots__/compiler.test.ts.snap

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,33 @@
22

33
exports[`@.caml:{'no apples'} | {0} apple | {n} apples: code 1`] = `
44
"function __msg__ (ctx) {
5+
const { normalize: _normalize, modifier: _modifier, type: _type, message: _message, interpolate: _interpolate, list: _list, named: _named, pluralIndex: _pluralIndex, pluralRule: _pluralRule, orgPluralRule: _orgPluralRule } = ctx
56
return [
6-
ctx.normalize([
7-
ctx.modifier(\\"caml\\")(ctx.message(\\"no apples\\")(ctx), ctx.type)
8-
]), ctx.normalize([
9-
ctx.interpolate(ctx.list(0)), \\" apple\\"
10-
]), ctx.normalize([
11-
ctx.interpolate(ctx.named(\\"n\\")), \\" apples\\"
7+
_normalize([
8+
_modifier(\\"caml\\")(_message(\\"no apples\\")(ctx), _type)
9+
]), _normalize([
10+
_interpolate(_list(0)), \\" apple\\"
11+
]), _normalize([
12+
_interpolate(_named(\\"n\\")), \\" apples\\"
1213
])
13-
][ctx.pluralRule(ctx.pluralIndex, 3, ctx.orgPluralRule)]
14+
][_pluralRule(_pluralIndex, 3, _orgPluralRule)]
1415
}"
1516
`;
1617

1718
exports[`edge cases | | | : code 1`] = `
1819
"function __msg__ (ctx) {
20+
const { normalize: _normalize, pluralIndex: _pluralIndex, pluralRule: _pluralRule, orgPluralRule: _orgPluralRule } = ctx
1921
return [
20-
ctx.normalize([
22+
_normalize([
2123
22-
]), ctx.normalize([
24+
]), _normalize([
2325
24-
]), ctx.normalize([
26+
]), _normalize([
2527
26-
]), ctx.normalize([
28+
]), _normalize([
2729
2830
])
29-
][ctx.pluralRule(ctx.pluralIndex, 4, ctx.orgPluralRule)]
31+
][_pluralRule(_pluralIndex, 4, _orgPluralRule)]
3032
}"
3133
`;
3234

@@ -52,15 +54,17 @@ Object {
5254

5355
exports[`warnHtmlMessage default: code 1`] = `
5456
"function __msg__ (ctx) {
55-
return ctx.normalize([
57+
const { normalize: _normalize } = ctx
58+
return _normalize([
5659
\\"<p>hello</p>\\"
5760
])
5861
}"
5962
`;
6063

6164
exports[`warnHtmlMessage false: code 1`] = `
6265
"function __msg__ (ctx) {
63-
return ctx.normalize([
66+
const { normalize: _normalize } = ctx
67+
return _normalize([
6468
\\"<p>hello</p>\\"
6569
])
6670
}"

0 commit comments

Comments
 (0)