1 # Copyright 2009 The Closure Library Authors. All Rights Reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS-IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 """Scans a source JS file for its provided and required namespaces.
18 Simple class to scan a JavaScript file and express its dependencies.
21 __author__
= 'nnaze@google.com'
26 _BASE_REGEX_STRING
= r
'^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s
*\
)'
27 _MODULE_REGEX = re.compile(_BASE_REGEX_STRING % 'module
')
28 _PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide
')
30 _REQUIRE_REGEX_STRING = (r'^\s
*(?
:(?
:var|let|const
)\s
+[a
-zA
-Z_$
][a
-zA
-Z0
-9$_
]*'
31 r'\s
*=\s
*)?goog\
.require\
(\s
*[\'"](.+)[\'"]\s
*\
)')
32 _REQUIRES_REGEX = re.compile(_REQUIRE_REGEX_STRING)
36 """Scans a JavaScript source for its provided and required namespaces."""
38 # Matches a "/* ... */" comment.
39 # Note: We can't definitively distinguish a
"/*" in a string literal without a
40 # state machine tokenizer. We'll assume that a line starting with whitespace
41 # and "/*" is a comment.
42 _COMMENT_REGEX
= re
.compile(
44 ^\s* # Start of a new line and whitespace
46 .*? # Non greedy match of any characters (including newlines)
48 re
.MULTILINE | re
.DOTALL | re
.VERBOSE
)
50 def __init__(self
, source
):
51 """Initialize a source.
54 source: str, The JavaScript source.
59 self
.is_goog_module
= False
65 """Get the source as a string."""
69 def _StripComments(cls
, source
):
70 return cls
._COMMENT
_REGEX
.sub('', source
)
73 def _HasProvideGoogFlag(cls
, source
):
74 """Determines whether the @provideGoog flag is in a comment."""
75 for comment_content
in cls
._COMMENT
_REGEX
.findall(source
):
76 if '@provideGoog' in comment_content
:
81 def _ScanSource(self
):
82 """Fill in provides and requires by scanning the source."""
84 stripped_source
= self
._StripComments
(self
.GetSource())
86 source_lines
= stripped_source
.splitlines()
87 for line
in source_lines
:
88 match
= _PROVIDE_REGEX
.match(line
)
90 self
.provides
.add(match
.group(1))
91 match
= _MODULE_REGEX
.match(line
)
93 self
.provides
.add(match
.group(1))
94 self
.is_goog_module
= True
95 match
= _REQUIRES_REGEX
.match(line
)
97 self
.requires
.add(match
.group(1))
99 # Closure's base file implicitly provides 'goog'.
100 # This is indicated with the @provideGoog flag.
101 if self
._HasProvideGoogFlag
(self
.GetSource()):
103 if len(self
.provides
) or len(self
.requires
):
105 'Base file should not provide or require namespaces.')
107 self
.provides
.add('goog')
110 def GetFileContents(path
):
111 """Get a file's contents as a string.
114 path: str, Path to file.
117 str, Contents of file.
120 IOError: An error occurred opening or reading the file.
125 return fileobj
.read()