Skip to content

Commit 896b1de

Browse files
authored
feat: support AST minification (#1474)
1 parent a368ac8 commit 896b1de

File tree

10 files changed

+299
-31
lines changed

10 files changed

+299
-31
lines changed

packages/core-base/src/format.ts

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
LinkedNode,
1111
LinkedKeyNode,
1212
LinkedModifierNode,
13+
PluralNode,
1314
ResourceNode
1415
} from '@intlify/message-compiler'
1516
import type {
@@ -24,18 +25,19 @@ export function format<Message = string>(
2425
): MessageFunction<Message> {
2526
const msg = (ctx: MessageContext<Message>): MessageFunctionReturn<Message> =>
2627
formatParts<Message>(ctx, ast)
27-
// TODO: add meta data for vue-devtools debugging, such as `key`, `source` and `locale`
28-
// TODO: optimization for static text message
2928
return msg
3029
}
3130

3231
function formatParts<Message = string>(
3332
ctx: MessageContext<Message>,
3433
ast: ResourceNode
3534
): MessageFunctionReturn<Message> {
36-
if (ast.body.type === NodeTypes.Plural) {
35+
const body = ast.b || ast.body
36+
if ((body.t || body.type) === NodeTypes.Plural) {
37+
const plural = body as PluralNode
38+
const cases = plural.c || plural.cases
3739
return ctx.plural(
38-
ast.body.cases.reduce(
40+
cases.reduce(
3941
(messages, c) =>
4042
[
4143
...messages,
@@ -45,20 +47,21 @@ function formatParts<Message = string>(
4547
) as Message[]
4648
) as MessageFunctionReturn<Message>
4749
} else {
48-
return formatMessageParts(ctx, ast.body)
50+
return formatMessageParts(ctx, body as MessageNode)
4951
}
5052
}
5153

5254
function formatMessageParts<Message = string>(
5355
ctx: MessageContext<Message>,
5456
node: MessageNode
5557
): MessageFunctionReturn<Message> {
56-
if (node.static) {
58+
const _static = node.s || node.static
59+
if (_static) {
5760
return ctx.type === 'text'
58-
? (node.static as MessageFunctionReturn<Message>)
59-
: ctx.normalize([node.static] as MessageType<Message>[])
61+
? (_static as MessageFunctionReturn<Message>)
62+
: ctx.normalize([_static] as MessageType<Message>[])
6063
} else {
61-
const messages = node.items.reduce(
64+
const messages = (node.i || node.items).reduce(
6265
(acm, c) => [...acm, formatMessagePart(ctx, c)],
6366
[] as MessageType<Message>[]
6467
)
@@ -70,30 +73,35 @@ function formatMessagePart<Message = string>(
7073
ctx: MessageContext<Message>,
7174
node: Node
7275
): MessageType<Message> {
73-
switch (node.type) {
76+
const type = node.t || node.type
77+
switch (type) {
7478
case NodeTypes.Text:
75-
return (node as TextNode).value as MessageType<Message>
79+
const text = node as TextNode
80+
return (text.v || text.value) as MessageType<Message>
7681
case NodeTypes.Literal:
77-
return (node as LiteralNode).value as MessageType<Message>
82+
const literal = node as LiteralNode
83+
return (literal.v || literal.value) as MessageType<Message>
7884
case NodeTypes.Named:
79-
return ctx.interpolate(ctx.named((node as NamedNode).key))
85+
const named = node as NamedNode
86+
return ctx.interpolate(ctx.named(named.k || named.key))
8087
case NodeTypes.List:
81-
return ctx.interpolate(ctx.list((node as ListNode).index))
88+
const list = node as ListNode
89+
return ctx.interpolate(ctx.list(list.i || list.index))
8290
case NodeTypes.Linked:
91+
const linked = node as LinkedNode
92+
const modifier = linked.m || linked.modifier
8393
return ctx.linked(
84-
formatMessagePart(ctx, (node as LinkedNode).key) as string,
85-
(node as LinkedNode).modifier
86-
? (formatMessagePart(ctx, (node as LinkedNode).modifier!) as string)
87-
: undefined,
94+
formatMessagePart(ctx, linked.k || linked.key) as string,
95+
modifier ? (formatMessagePart(ctx, modifier) as string) : undefined,
8896
ctx.type
8997
)
9098
case NodeTypes.LinkedKey:
91-
return (node as LinkedKeyNode).value as MessageType<Message>
99+
const linkedKey = node as LinkedKeyNode
100+
return (linkedKey.v || linkedKey.value) as MessageType<Message>
92101
case NodeTypes.LinkedModifier:
93-
return (node as LinkedModifierNode).value as MessageType<Message>
102+
const linkedModifier = node as LinkedModifierNode
103+
return (linkedModifier.v || linkedModifier.value) as MessageType<Message>
94104
default:
95-
throw new Error(
96-
`unhandled node type on format message part: ${node.type}`
97-
)
105+
throw new Error(`unhandled node type on format message part: ${type}`)
98106
}
99107
}

packages/core-base/test/compilation.test.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,31 @@ describe('compile', () => {
3737
expect(msg(ctx)).toBe('hello kazupon!')
3838
})
3939

40-
test('AST passing', () => {
41-
const { ast } = baseCompile('hello {name}!')
42-
const msg = compile(ast)
43-
const ctx = context({
44-
named: { name: 'kazupon' }
40+
describe('AST', () => {
41+
test('basic', () => {
42+
const { ast } = baseCompile('hello {name}!', {
43+
location: false,
44+
jit: true
45+
})
46+
const msg = compile(ast)
47+
const ctx = context({
48+
named: { name: 'kazupon' }
49+
})
50+
expect(msg(ctx)).toBe('hello kazupon!')
51+
})
52+
53+
test('minify', () => {
54+
const { ast } = baseCompile('hello {name}!', {
55+
location: false,
56+
jit: true,
57+
minify: true
58+
})
59+
const msg = compile(ast)
60+
const ctx = context({
61+
named: { name: 'kazupon' }
62+
})
63+
expect(msg(ctx)).toBe('hello kazupon!')
4564
})
46-
expect(msg(ctx)).toBe('hello kazupon!')
4765
})
4866

4967
test('error', () => {

packages/message-compiler/src/compiler.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createParser } from './parser'
22
import { transform } from './transformer'
33
import { optimize } from './optimizer'
4+
import { minify } from './minifier'
45
import { generate } from './generator'
56
import { assign } from '@intlify/shared'
67

@@ -15,7 +16,8 @@ export function baseCompile(
1516
): CompilerResult {
1617
const assignedOptions = assign({}, options)
1718
const jit = !!assignedOptions.jit
18-
const doOptimize =
19+
const enalbeMinify = !!assignedOptions.minify
20+
const enambeOptimize =
1921
assignedOptions.optimize == null ? true : assignedOptions.optimize
2022

2123
// parse source codes
@@ -30,7 +32,10 @@ export function baseCompile(
3032
return generate(ast, assignedOptions)
3133
} else {
3234
// optimize ASTs
33-
doOptimize && optimize(ast)
35+
enambeOptimize && optimize(ast)
36+
37+
// minimize ASTs
38+
enalbeMinify && minify(ast)
3439

3540
// In JIT mode, no ast transform, no code generation.
3641
return { ast, code: '' }
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { NodeTypes } from './nodes'
2+
3+
import type {
4+
MessageNode,
5+
ResourceNode,
6+
Node,
7+
PluralNode,
8+
TextNode,
9+
LiteralNode,
10+
LinkedNode,
11+
LinkedKeyNode,
12+
LinkedModifierNode,
13+
ListNode,
14+
NamedNode
15+
} from './nodes'
16+
17+
/* eslint-disable @typescript-eslint/no-explicit-any */
18+
19+
export function minify(node: Node) {
20+
node.t = node.type
21+
22+
switch (node.type) {
23+
case NodeTypes.Resource:
24+
const resource = node as ResourceNode
25+
minify(resource.body)
26+
resource.b = resource.body
27+
delete (resource as any).body
28+
break
29+
case NodeTypes.Plural:
30+
const plural = node as PluralNode
31+
const cases = plural.cases
32+
for (let i = 0; i < cases.length; i++) {
33+
minify(cases[i])
34+
}
35+
plural.c = cases
36+
delete (plural as any).cases
37+
break
38+
case NodeTypes.Message:
39+
const message = node as MessageNode
40+
const items = message.items
41+
for (let i = 0; i < items.length; i++) {
42+
minify(items[i])
43+
}
44+
message.i = items
45+
delete (message as any).items
46+
if (message.static) {
47+
message.s = message.static
48+
delete (message as any).static
49+
}
50+
break
51+
case NodeTypes.Text:
52+
case NodeTypes.Literal:
53+
case NodeTypes.LinkedModifier:
54+
case NodeTypes.LinkedKey:
55+
const valueNode = node as
56+
| TextNode
57+
| LiteralNode
58+
| LinkedKeyNode
59+
| LinkedModifierNode
60+
if (valueNode.value) {
61+
valueNode.v = valueNode.value
62+
delete (valueNode as any).value
63+
}
64+
break
65+
case NodeTypes.Linked:
66+
const linked = node as LinkedNode
67+
minify(linked.key)
68+
linked.k = linked.key
69+
delete (linked as any).key
70+
if (linked.modifier) {
71+
minify(linked.modifier)
72+
linked.m = linked.modifier
73+
delete (linked as any).modifier
74+
}
75+
break
76+
case NodeTypes.List:
77+
const list = node as ListNode
78+
list.i = list.index
79+
delete (list as any).index
80+
break
81+
case NodeTypes.Named:
82+
const named = node as NamedNode
83+
named.k = named.key
84+
delete (named as any).key
85+
break
86+
default:
87+
if (__DEV__) {
88+
throw new Error(`unhandled minify node type: ${node.type}`)
89+
}
90+
}
91+
92+
delete (node as any).type
93+
}
94+
95+
/* eslint-enable @typescript-eslint/no-explicit-any */

packages/message-compiler/src/nodes.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export type Identifier = string
1818

1919
export interface Node {
2020
type: NodeTypes
21+
/**
22+
* @internal `type` alias
23+
*/
24+
t?: NodeTypes
2125
start?: number
2226
end?: number
2327
loc?: SourceLocation
@@ -26,19 +30,35 @@ export interface Node {
2630
export interface ResourceNode extends Node {
2731
type: NodeTypes.Resource
2832
body: MessageNode | PluralNode
33+
/**
34+
* @internal `body` alias
35+
*/
36+
b?: MessageNode | PluralNode
2937
cacheKey?: string
3038
helpers?: string[]
3139
}
3240

3341
export interface PluralNode extends Node {
3442
type: NodeTypes.Plural
3543
cases: MessageNode[]
44+
/**
45+
* @internal `cases` alias
46+
*/
47+
c?: MessageNode[]
3648
}
3749

3850
export interface MessageNode extends Node {
3951
type: NodeTypes.Message
4052
static?: string
53+
/**
54+
* @internal `static` alias
55+
*/
56+
s?: string
4157
items: MessageElementNode[]
58+
/**
59+
* @internal `items` alias
60+
*/
61+
i?: MessageElementNode[]
4262
}
4363

4464
type MessageElementNode =
@@ -51,35 +71,67 @@ type MessageElementNode =
5171
export interface TextNode extends Node {
5272
type: NodeTypes.Text
5373
value?: string
74+
/**
75+
* @internal `value` alias
76+
*/
77+
v?: string
5478
}
5579

5680
export interface NamedNode extends Node {
5781
type: NodeTypes.Named
5882
key: Identifier
83+
/**
84+
* @internal `key` alias
85+
*/
86+
k?: Identifier
5987
}
6088

6189
export interface ListNode extends Node {
6290
type: NodeTypes.List
6391
index: number
92+
/**
93+
* @internal `index` alias
94+
*/
95+
i?: number
6496
}
6597

6698
export interface LiteralNode extends Node {
6799
type: NodeTypes.Literal
68100
value?: string
101+
/**
102+
* @internal `value` alias
103+
*/
104+
v?: string
69105
}
70106

71107
export interface LinkedNode extends Node {
72108
type: NodeTypes.Linked
73109
modifier?: LinkedModifierNode
110+
/**
111+
* @internal `modifier` alias
112+
*/
113+
m?: LinkedModifierNode
74114
key: LinkedKeyNode | NamedNode | ListNode | LiteralNode
115+
/**
116+
* @internal `key` alias
117+
*/
118+
k?: LinkedKeyNode | NamedNode | ListNode | LiteralNode
75119
}
76120

77121
export interface LinkedKeyNode extends Node {
78122
type: NodeTypes.LinkedKey
79123
value: string
124+
/**
125+
* @internal `value` alias
126+
*/
127+
v?: string
80128
}
81129

82130
export interface LinkedModifierNode extends Node {
83131
type: NodeTypes.LinkedModifier
84132
value: Identifier
133+
/**
134+
* @internal `value` alias
135+
*/
136+
v?: Identifier
85137
}

packages/message-compiler/src/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export interface CodeGenOptions {
3434

3535
export type CompileOptions = {
3636
optimize?: boolean // default true
37+
minify?: boolean // default false
3738
jit?: boolean // default false
3839
} & TransformOptions &
3940
CodeGenOptions &

0 commit comments

Comments
 (0)