UNPKG

4.64 kBJavaScriptView Raw
1// Copyright 2015 Esri
2// Licensed under The MIT License(MIT);
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5// http://opensource.org/licenses/MIT
6// Unless required by applicable law or agreed to in writing, software
7// distributed under the License is distributed on an "AS IS" BASIS,
8// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9// See the License for the specific language governing permissions and
10// limitations under the License.
11
12/* eslint-env node */
13'use strict';
14
15const {
16 parse
17} = require('@babel/parser');
18const traverse = require('@babel/traverse').default;
19const t = require('@babel/types');
20const generator = require('@babel/generator').default;
21
22// Replace indentifier
23const Identifiers = {
24 'require': 'eriuqer',
25 'define': 'enifed'
26};
27
28// Use babel to parse/traverse/generate code.
29// - replace define and require with enifed and eriuqer
30// - if required, collect the external AMD modules referenced
31
32module.exports = function replaceRequireAndDefine(code, amdPackages, externalAmdModules) {
33
34 const ast = parse(code);
35
36 traverse(ast, {
37 Program(path) {
38 // This will take care of the loader.js code where define and require are define globally
39 // The cool thing is that babel will rename all references as well
40 path.scope.rename('define', Identifiers.define);
41 path.scope.rename('require', Identifiers.require);
42 },
43 CallExpression(path) {
44
45 // Looking for:
46 // - call for the global define function. If it matches then rename it and collect the external AMD references
47 // - call for the global require function. It it matches then rename it
48 // - eval calls: We need to process the eval code itself (used by auto-import)
49 const {
50 node
51 } = path;
52
53 // Looking for define('foo', ['a', 'b'], function(a, b) {})
54 if (t.isIdentifier(node.callee, {
55 name: 'define'
56 })) {
57 if (node.arguments.length < 2 || !t.isArrayExpression(node.arguments[1])) {
58 return;
59 }
60
61 // Rename if it's invoking a global define function
62 if (!path.scope.hasBinding('define')) {
63 node.callee.name = Identifiers.define;
64 }
65
66 // Collect external AMD references
67 if (!amdPackages || !externalAmdModules) {
68 return;
69 }
70
71 node.arguments[1].elements.forEach(function (element) {
72 if (!t.isStringLiteral(element)) {
73 return;
74 }
75
76 const isExternalAmd = amdPackages.some(function (amdPackage) {
77 return element.value.indexOf(amdPackage + '/') === 0 || element.value === amdPackage;
78 });
79
80 if (!isExternalAmd) {
81 return;
82 }
83
84 externalAmdModules.add(element.value);
85
86 });
87 return;
88 }
89
90 // Rename if it's invoking a global require function
91 if (t.isIdentifier(node.callee, {
92 name: 'require'
93 }) && !path.scope.hasBinding('require')) {
94 node.callee.name = Identifiers.require;
95 }
96
97 // auto-import injects eval expression. We need to process them as individual code
98 if (t.isIdentifier(node.callee, {
99 name: 'eval'
100 }) && t.isStringLiteral(node.arguments[0])) {
101 node.arguments[0].value = replaceRequireAndDefine(node.arguments[0].value, amdPackages, externalAmdModules);
102 }
103 },
104 VariableDeclarator(path) {
105 // This could happened in auto-import eval: var d = define;
106 if (t.isIdentifier(path.node.init, {
107 name: 'define'
108 }) && !path.scope.hasBinding('define')) {
109 path.node.init.name = Identifiers.define;
110 return;
111 }
112 // This could happened in auto-import eval: var r = require;
113 if (t.isIdentifier(path.node.init, {
114 name: 'require'
115 }) && !path.scope.hasBinding('require')) {
116 path.node.init.name = Identifiers.require;
117 }
118 },
119 AssignmentExpression(path) {
120 // This could happened in auto-import eval: window.d = define;
121 if (t.isIdentifier(path.node.right, {
122 name: 'define'
123 }) && !path.scope.hasBinding('define')) {
124 path.node.right.name = Identifiers.define;
125 return;
126 }
127 // This could happened in auto-import eval: window.r = require;
128 if (t.isIdentifier(path.node.right, {
129 name: 'require'
130 }) && !path.scope.hasBinding('require')) {
131 path.node.right.name = Identifiers.require;
132 }
133 },
134
135 });
136
137 return generator(ast, {
138 retainLines: true,
139 retainFunctionParens: true
140 }, code).code;
141}