forked from Automattic/wp-calypso
-
Notifications
You must be signed in to change notification settings - Fork 0
/
inline-imports.js
103 lines (92 loc) · 3.2 KB
/
inline-imports.js
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
/** @format */
/**
* Inlines Redux action type constants with their string value
* Babel Transform
*
* We use named constants for our Redux action types for a number
* of reasons that aid our ability to manage and understand these
* actions. The constants help us find uses of a given action type
* and they limit the effect of action type typos because a type
* which is improperly written will result in an import error
* instead of a valid-but-useless string.
*
* However, none of this information is helpful or valuable to our
* customers after the bundle has been build. The values of the
* constants are meaningless in essence though they can be useful
* for debugging if they match something searchable in our code.
* Mostly the constants add bloat to the bundle without value.
*
* This transform replaces those constants with their string
* values directly, inlining those values, in order to eliminate
* our need to ship the "action type dictionary" mapping the
* constants with their values.
*
* @example
* Input:
* import { INCREMENT as INC, RESET } from 'state/action-types';
*
* if ( INC === type ) { return state + 1 }
* if ( RESET === type ) { return 0 }
*
* Output:
* // no more import statement
*
* if ( 'INCREMENT' === type ) { return state + 1 }
* if ( 'RESET' === type ) { return 0 }
*
* We can note a few important points:
* - This inlines the actual constant name, not the imported-as name
* - It ultimately doesn't matter what the values are as long as they
* are unique. We have less risk of bugs occurring as a result of
* action type clashes after this transform than we do before because
* our imports are constrained to be unique while the string values
* of our action types aren't.
* - The end result of this transform is the elimination of the
* ` `state/action-types` module from the final output bundle.
*
* @param {Object} babel exposed Babel API
* @returns {Object} the Babel transformer
*/
function transformIt( babel ) {
const { types: t } = babel;
const replacer = {
Identifier( path ) {
// we haven't deleted the `import` statement yet
// so if we don't skip it then we'll enter a cycle
if ( t.isImportSpecifier( path.parentPath ) ) {
return path.skip();
}
const name = path.node.name;
if ( ! this.myTypes.hasOwnProperty( name ) ) {
return path.skip();
}
path.replaceWith( t.stringLiteral( this.myTypes[ name ] ) );
},
};
const mergeImports = ( o, n ) => {
o[ n.local.name ] = n.imported.name;
return o;
};
return {
name: 'action-type-inliner',
visitor: {
ImportDeclaration( path ) {
// import … from '{ path.node.source.value }'
const name = path.node.source.value;
// this is a very-specific transform because
// we don't want to mess up other imports
if ( name !== 'state/action-types' ) {
return path.skip();
}
const myTypes = path.node.specifiers
.filter( t.isImportSpecifier )
.reduce( mergeImports, {} );
path.parentPath.traverse( replacer, { myTypes } );
// and remove the `import` statement after we have
// made our replacements in the module
path.remove();
},
},
};
}
module.exports = transformIt;