1
- const fs = require ( 'fs' )
2
- const helperModuleImports = require ( '@babel/helper-module-imports' )
1
+ import type { Node , PluginObj , PluginPass } from '@babel/core'
2
+ import * as helperModuleImports from '@babel/helper-module-imports'
3
+ import * as fs from 'node:fs'
4
+
5
+ type Babel = typeof import ( '@babel/core' )
3
6
4
7
/**
5
- * Converts an AST type into a javascript string so that it can be added to the error message lookup.
8
+ * Represents the options for the {@linkcode mangleErrorsPlugin}.
9
+ *
10
+ * @internal
11
+ */
12
+ export interface MangleErrorsPluginOptions {
13
+ /**
14
+ * Whether to minify the error messages or not.
15
+ * If `true`, the error messages will be replaced with an index
16
+ * that maps object lookup.
17
+ */
18
+ minify : boolean
19
+ }
20
+
21
+ /**
22
+ * Converts an AST type into a JavaScript string so that it can be added to the error message lookup.
6
23
*
7
24
* Adapted from React (https://github.com/facebook/react/blob/master/scripts/shared/evalToString.js) with some
8
- * adjustments
25
+ * adjustments.
9
26
*/
10
- const evalToString = ast => {
27
+ const evalToString = (
28
+ ast : Node | { type : 'Literal' ; value : string }
29
+ ) : string => {
11
30
switch ( ast . type ) {
12
31
case 'StringLiteral' :
13
32
case 'Literal' : // ESLint
@@ -31,14 +50,14 @@ const evalToString = ast => {
31
50
}
32
51
33
52
/**
34
- * Takes a `throw new error ` statement and transforms it depending on the minify argument. Either option results in a
35
- * smaller bundle size in production for consumers .
53
+ * Transforms a `throw new Error ` statement based on the ` minify` argument, resulting in a smaller bundle size
54
+ * for consumers in production.
36
55
*
37
- * If minify is enabled, we'll replace the error message with just an index that maps to an arrow object lookup.
56
+ * If ` minify` is enabled, the error message will be replaced with an index that maps to an object lookup.
38
57
*
39
- * If minify is disabled, we'll add in a conditional statement to check the process.env.NODE_ENV which will output a
40
- * an error number index in production or the actual error message in development. This allows consumers using webpack
41
- * or another build tool to have these messages in development but have just the error index in production.
58
+ * If ` minify` is disabled, a conditional statement will be added to check ` process.env.NODE_ENV`, which will output
59
+ * an error number index in production or the actual error message in development. This allows consumers using Webpack
60
+ * or another build tool to have these messages in development but only the error index in production.
42
61
*
43
62
* E.g.
44
63
* Before:
@@ -49,30 +68,40 @@ const evalToString = ast => {
49
68
* throw new Error(0);
50
69
* throw new Error(1);
51
70
*
52
- * After: (without minify):
53
- * throw new Error(node. process.NODE_ENV === 'production' ? 0 : "This is my error message.");
54
- * throw new Error(node. process.NODE_ENV === 'production' ? 1 : "This is a second error message.");
71
+ * After (without minify):
72
+ * throw new Error(process.env .NODE_ENV === 'production' ? 0 : "This is my error message.");
73
+ * throw new Error(process.env .NODE_ENV === 'production' ? 1 : "This is a second error message.");
55
74
*/
56
- module . exports = babel => {
75
+ export const mangleErrorsPlugin = (
76
+ babel : Babel ,
77
+ options : MangleErrorsPluginOptions
78
+ ) : PluginObj < PluginPass & MangleErrorsPluginOptions > => {
57
79
const t = babel . types
58
80
// When the plugin starts up, we'll load in the existing file. This allows us to continually add to it so that the
59
81
// indexes do not change between builds.
60
82
let errorsFiles = ''
61
83
if ( fs . existsSync ( 'errors.json' ) ) {
62
84
errorsFiles = fs . readFileSync ( 'errors.json' ) . toString ( )
63
85
}
64
- let errors = Object . values ( JSON . parse ( errorsFiles || '{}' ) )
86
+ const errors = Object . values ( JSON . parse ( errorsFiles || '{}' ) )
65
87
// This variable allows us to skip writing back to the file if the errors array hasn't changed
66
88
let changeInArray = false
67
89
68
90
return {
91
+ name : 'mangle-errors-plugin' ,
69
92
pre : ( ) => {
70
93
changeInArray = false
71
94
} ,
72
95
visitor : {
73
- ThrowStatement ( path , file ) {
96
+ ThrowStatement ( path ) {
97
+ if (
98
+ ! ( 'arguments' in path . node . argument ) ||
99
+ ! t . isNewExpression ( path . node . argument )
100
+ ) {
101
+ return
102
+ }
74
103
const args = path . node . argument . arguments
75
- const minify = file . opts . minify
104
+ const { minify } = options
76
105
77
106
if ( args && args [ 0 ] ) {
78
107
// Skip running this logic when certain types come up:
@@ -82,11 +111,15 @@ module.exports = babel => {
82
111
path . node . argument . arguments [ 0 ] . type === 'Identifier' ||
83
112
path . node . argument . arguments [ 0 ] . type === 'NumericLiteral' ||
84
113
path . node . argument . arguments [ 0 ] . type === 'ConditionalExpression' ||
85
- path . node . argument . arguments [ 0 ] . type === 'CallExpression'
114
+ path . node . argument . arguments [ 0 ] . type === 'CallExpression' ||
115
+ ! t . isExpression ( path . node . argument . arguments [ 0 ] ) ||
116
+ ! t . isIdentifier ( path . node . argument . callee )
86
117
) {
87
118
return
88
119
}
89
120
121
+ const errorName = path . node . argument . callee . name
122
+
90
123
const errorMsgLiteral = evalToString ( path . node . argument . arguments [ 0 ] )
91
124
92
125
if ( errorMsgLiteral . includes ( 'Super expression' ) ) {
@@ -106,7 +139,7 @@ module.exports = babel => {
106
139
const formatProdErrorMessageIdentifier = helperModuleImports . addNamed (
107
140
path ,
108
141
'formatProdErrorMessage' ,
109
- 'src /utils/formatProdErrorMessage' ,
142
+ '@internal /utils/formatProdErrorMessage' ,
110
143
{ nameHint : 'formatProdErrorMessage' }
111
144
)
112
145
@@ -119,13 +152,13 @@ module.exports = babel => {
119
152
if ( minify ) {
120
153
path . replaceWith (
121
154
t . throwStatement (
122
- t . newExpression ( t . identifier ( 'Error' ) , [ prodMessage ] )
155
+ t . newExpression ( t . identifier ( errorName ) , [ prodMessage ] )
123
156
)
124
157
)
125
158
} else {
126
159
path . replaceWith (
127
160
t . throwStatement (
128
- t . newExpression ( t . identifier ( 'Error' ) , [
161
+ t . newExpression ( t . identifier ( errorName ) , [
129
162
t . conditionalExpression (
130
163
t . binaryExpression (
131
164
'===' ,
@@ -150,3 +183,5 @@ module.exports = babel => {
150
183
}
151
184
}
152
185
}
186
+
187
+ export default mangleErrorsPlugin
0 commit comments