-
-
Notifications
You must be signed in to change notification settings - Fork 355
/
Copy pathcompilation.ts
129 lines (112 loc) · 3.48 KB
/
compilation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {
createParser,
defaultOnError,
detectHtmlTag,
mangle,
optimize
} from '@intlify/message-compiler'
import { create, format, isBoolean, isString, warn } from '@intlify/shared'
import { isMessageAST } from './ast'
import { format as formatMessage } from './format'
import type {
CompileError,
CompileOptions,
CompilerResult,
ResourceNode
} from '@intlify/message-compiler'
import type { MessageCompilerContext } from './context'
import type { MessageFunction, MessageFunctions } from './runtime'
const WARN_MESSAGE = `Detected HTML in '{source}' message. Recommend not using HTML messages to avoid XSS.`
function checkHtmlMessage(source: string, warnHtmlMessage?: boolean): void {
if (warnHtmlMessage && detectHtmlTag(source)) {
warn(format(WARN_MESSAGE, { source }))
}
}
const defaultOnCacheKey = (message: string): string => message
let compileCache: unknown = create()
export function clearCompileCache(): void {
compileCache = create()
}
function baseCompile(
message: string,
options: CompileOptions = {}
): CompilerResult & { detectError: boolean } {
// error detecting on compile
let detectError = false
const onError = options.onError || defaultOnError
options.onError = (err: CompileError): void => {
detectError = true
onError(err)
}
// parse source codes
const parser = createParser(options)
const ast = parser.parse(message)
// optimize ASTs
options.optimize && optimize(ast)
// minimize ASTs
options.mangle && mangle(ast)
return { ast, detectError, code: '' }
}
/* #__NO_SIDE_EFFECTS__ */
export function compile<
Message = string,
MessageSource = string | ResourceNode
>(
message: MessageSource,
context: MessageCompilerContext
): MessageFunction<Message> {
if (
!__RUNTIME__ &&
(__ESM_BROWSER__ ||
__NODE_JS__ ||
__GLOBAL__ ||
!__FEATURE_DROP_MESSAGE_COMPILER__) &&
isString(message)
) {
// check HTML message
const warnHtmlMessage = isBoolean(context.warnHtmlMessage)
? context.warnHtmlMessage
: true
__DEV__ && checkHtmlMessage(message, warnHtmlMessage)
// check caches
const onCacheKey = context.onCacheKey || defaultOnCacheKey
const cacheKey = onCacheKey(message)
const cached = (compileCache as MessageFunctions<Message>)[cacheKey]
if (cached) {
return cached
}
// compile message
const { ast, detectError } = baseCompile(message, {
...context,
location: __DEV__,
mangle: !__DEV__,
optimize: !__DEV__
})
// compose message function from AST
const msg = formatMessage<Message>(ast)
// if occurred compile error, don't cache
return !detectError
? ((compileCache as MessageFunctions<Message>)[cacheKey] = msg)
: msg
} else {
if (__DEV__ && !isMessageAST(message)) {
warn(
`the message that is resolve with key '${context.key}' is not supported for jit compilation`
)
return (() => message) as MessageFunction<Message>
}
// AST case (passed from bundler)
const cacheKey = (message as unknown as ResourceNode).cacheKey
if (cacheKey) {
const cached = (compileCache as MessageFunctions<Message>)[cacheKey]
if (cached) {
return cached
}
// compose message function from message (AST)
return ((compileCache as MessageFunctions<Message>)[cacheKey] =
formatMessage<Message>(message as unknown as ResourceNode))
} else {
return formatMessage<Message>(message as unknown as ResourceNode)
}
}
}