Backed out changeset 8fc3326bce7f (bug 1943032) for causing failures at browser_tab_g...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / closure-library / closure / bin / scopify.py
blobd8057efbc9fa2aa01c6f0e9941f67ffc46d66f77
1 #!/usr/bin/python
3 # Copyright 2010 The Closure Library Authors. All Rights Reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS-IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 """Automatically converts codebases over to goog.scope.
20 Usage:
21 cd path/to/my/dir;
22 ../../../../javascript/closure/bin/scopify.py
24 Scans every file in this directory, recursively. Looks for existing
25 goog.scope calls, and goog.require'd symbols. If it makes sense to
26 generate a goog.scope call for the file, then we will do so, and
27 try to auto-generate some aliases based on the goog.require'd symbols.
29 Known Issues:
31 When a file is goog.scope'd, the file contents will be indented +2.
32 This may put some lines over 80 chars. These will need to be fixed manually.
34 We will only try to create aliases for capitalized names. We do not check
35 to see if those names will conflict with any existing locals.
37 This creates merge conflicts for every line of every outstanding change.
38 If you intend to run this on your codebase, make sure your team members
39 know. Better yet, send them this script so that they can scopify their
40 outstanding changes and "accept theirs".
42 When an alias is "captured", it can no longer be stubbed out for testing.
43 Run your tests.
45 """
47 __author__ = 'nicksantos@google.com (Nick Santos)'
49 import os.path
50 import re
51 import sys
53 REQUIRES_RE = re.compile(r"goog.require\('([^']*)'\)")
55 # Edit this manually if you want something to "always" be aliased.
56 # TODO(nicksantos): Add a flag for this.
57 DEFAULT_ALIASES = {}
59 def Transform(lines):
60 """Converts the contents of a file into javascript that uses goog.scope.
62 Arguments:
63 lines: A list of strings, corresponding to each line of the file.
64 Returns:
65 A new list of strings, or None if the file was not modified.
66 """
67 requires = []
69 # Do an initial scan to be sure that this file can be processed.
70 for line in lines:
71 # Skip this file if it has already been scopified.
72 if line.find('goog.scope') != -1:
73 return None
75 # If there are any global vars or functions, then we also have
76 # to skip the whole file. We might be able to deal with this
77 # more elegantly.
78 if line.find('var ') == 0 or line.find('function ') == 0:
79 return None
81 for match in REQUIRES_RE.finditer(line):
82 requires.append(match.group(1))
84 if len(requires) == 0:
85 return None
87 # Backwards-sort the requires, so that when one is a substring of another,
88 # we match the longer one first.
89 for val in DEFAULT_ALIASES.values():
90 if requires.count(val) == 0:
91 requires.append(val)
93 requires.sort()
94 requires.reverse()
96 # Generate a map of requires to their aliases
97 aliases_to_globals = DEFAULT_ALIASES.copy()
98 for req in requires:
99 index = req.rfind('.')
100 if index == -1:
101 alias = req
102 else:
103 alias = req[(index + 1):]
105 # Don't scopify lowercase namespaces, because they may conflict with
106 # local variables.
107 if alias[0].isupper():
108 aliases_to_globals[alias] = req
110 aliases_to_matchers = {}
111 globals_to_aliases = {}
112 for alias, symbol in aliases_to_globals.items():
113 globals_to_aliases[symbol] = alias
114 aliases_to_matchers[alias] = re.compile('\\b%s\\b' % symbol)
116 # Insert a goog.scope that aliases all required symbols.
117 result = []
119 START = 0
120 SEEN_REQUIRES = 1
121 IN_SCOPE = 2
123 mode = START
124 aliases_used = set()
125 insertion_index = None
126 num_blank_lines = 0
127 for line in lines:
128 if mode == START:
129 result.append(line)
131 if re.search(REQUIRES_RE, line):
132 mode = SEEN_REQUIRES
134 elif mode == SEEN_REQUIRES:
135 if (line and
136 not re.search(REQUIRES_RE, line) and
137 not line.isspace()):
138 # There should be two blank lines before goog.scope
139 result += ['\n'] * 2
140 result.append('goog.scope(function() {\n')
141 insertion_index = len(result)
142 result += ['\n'] * num_blank_lines
143 mode = IN_SCOPE
144 elif line.isspace():
145 # Keep track of the number of blank lines before each block of code so
146 # that we can move them after the goog.scope line if necessary.
147 num_blank_lines += 1
148 else:
149 # Print the blank lines we saw before this code block
150 result += ['\n'] * num_blank_lines
151 num_blank_lines = 0
152 result.append(line)
154 if mode == IN_SCOPE:
155 for symbol in requires:
156 if not symbol in globals_to_aliases:
157 continue
159 alias = globals_to_aliases[symbol]
160 matcher = aliases_to_matchers[alias]
161 for match in matcher.finditer(line):
162 # Check to make sure we're not in a string.
163 # We do this by being as conservative as possible:
164 # if there are any quote or double quote characters
165 # before the symbol on this line, then bail out.
166 before_symbol = line[:match.start(0)]
167 if before_symbol.count('"') > 0 or before_symbol.count("'") > 0:
168 continue
170 line = line.replace(match.group(0), alias)
171 aliases_used.add(alias)
173 if line.isspace():
174 # Truncate all-whitespace lines
175 result.append('\n')
176 else:
177 result.append(line)
179 if len(aliases_used):
180 aliases_used = [alias for alias in aliases_used]
181 aliases_used.sort()
182 aliases_used.reverse()
183 for alias in aliases_used:
184 symbol = aliases_to_globals[alias]
185 result.insert(insertion_index,
186 'var %s = %s;\n' % (alias, symbol))
187 result.append('}); // goog.scope\n')
188 return result
189 else:
190 return None
192 def TransformFileAt(path):
193 """Converts a file into javascript that uses goog.scope.
195 Arguments:
196 path: A path to a file.
198 f = open(path)
199 lines = Transform(f.readlines())
200 if lines:
201 f = open(path, 'w')
202 for l in lines:
203 f.write(l)
204 f.close()
206 if __name__ == '__main__':
207 args = sys.argv[1:]
208 if not len(args):
209 args = '.'
211 for file_name in args:
212 if os.path.isdir(file_name):
213 for root, dirs, files in os.walk(file_name):
214 for name in files:
215 if name.endswith('.js') and \
216 not os.path.islink(os.path.join(root, name)):
217 TransformFileAt(os.path.join(root, name))
218 else:
219 if file_name.endswith('.js') and \
220 not os.path.islink(file_name):
221 TransformFileAt(file_name)