예제 #1
0
    def test_repr_compat(self):
        class fake_unicode(object):
            def __repr__(self):
                return "u'fake'"

        previous = utils.unicode
        self.addCleanup(setattr, utils, 'unicode', previous)

        utils.unicode = fake_unicode
        self.assertEqual("'fake'", utils.repr_compat(fake_unicode()))
        utils.unicode = None
        self.assertEqual("u'fake'", utils.repr_compat(fake_unicode()))
예제 #2
0
 def t_error(self, token):
     if self.cur_token:
         # TODO make use of the extended calling signature when done
         raise ECMASyntaxError('Illegal character %s at %s:%s after %s' % (
             repr_compat(token.value[0]),
             token.lineno,
             self._get_colno(token),
             format_lex_token(self.cur_token),
         ))
     else:
         raise ECMASyntaxError('Illegal character %s at %s:%s' % (
             repr_compat(token.value[0]),
             token.lineno,
             self._get_colno(token),
         ))
예제 #3
0
def broken_string_token_handler(lexer, token):
    match = PATT_BROKEN_STRING.match(token.value)
    if not match:
        return

    # update the error token value to only include what was matched here
    # as this will be the actual token that "failed"
    token.value = match.group()
    # calculate colno for current token colno before...
    colno = lexer._get_colno(token)
    # updating the newline indexes for the error reporting for raw
    # lexpos
    lexer._update_newline_idx(token)
    # probe for the next values (which no valid rules will match)
    position = lexer.lexer.lexpos + len(token.value)
    failure = lexer.lexer.lexdata[position:position + 2]
    if failure and failure[0] == '\\':
        type_ = {'x': 'hexadecimal', 'u': 'unicode'}[failure[1]]
        seq = re.match(r'\\[xu][0-9-a-f-A-F]*',
                       lexer.lexer.lexdata[position:]).group()
        raise ECMASyntaxError(
            "Invalid %s escape sequence '%s' at %s:%s" %
            (type_, seq, lexer.lineno, lexer._get_colno_lexpos(position)))
    tl = 16  # truncate length
    raise ECMASyntaxError(
        'Unterminated string literal %s at %s:%s' %
        (repr_compat(token.value[:tl].strip() +
                     (token.value[tl:] and '...')), token.lineno, colno))
예제 #4
0
    def walk(self,
             node,
             omit=('lexpos', 'lineno', 'colno', 'rowno'),
             indent=0,
             depth=-1,
             pos=False,
             _level=0):
        """
        Accepts the standard node argument, along with an optional omit
        flag - it should be an iterable that lists out all attributes
        that should be omitted from the repr output.
        """

        if not depth:
            return '<%s ...>' % node.__class__.__name__

        attrs = []
        children = node.children()
        ids = {id(child) for child in children}

        indentation = ' ' * (indent * (_level + 1))
        header = '\n' + indentation if indent else ''
        joiner = ',\n' + indentation if indent else ', '
        tailer = '\n' + ' ' * (indent * _level) if indent else ''

        for k, v in vars(node).items():
            if k.startswith('_'):
                continue
            if id(v) in ids:
                ids.remove(id(v))

            if isinstance(v, Node):
                attrs.append(
                    (k, self.walk(v, omit, indent, depth - 1, pos, _level)))
            elif isinstance(v, list):
                items = []
                for i in v:
                    if id(i) in ids:
                        ids.remove(id(i))
                    items.append(
                        self.walk(i, omit, indent, depth - 1, pos, _level + 1))
                attrs.append(
                    (k, '[' + header + joiner.join(items) + tailer + ']'))
            else:
                attrs.append((k, repr_compat(v)))

        if ids:
            # for unnamed child nodes.
            attrs.append(('?children', '[' + header + joiner.join(
                self.walk(child, omit, indent, depth - 1, pos, _level + 1)
                for child in children if id(child) in ids) + tailer + ']'))

        position = ('@%s:%s ' % (
            '?' if node.lineno is None else node.lineno,
            '?' if node.colno is None else node.colno,
        ) if pos else '')

        omit_keys = () if not omit else set(omit)
        return '<%s %s%s>' % (node.__class__.__name__, position, ', '.join(
            '%s=%s' % (k, v) for k, v in sorted(attrs) if k not in omit_keys))
예제 #5
0
def read(parser, stream):
    """
    Return an AST from the input ES5 stream.

    Arguments

    parser
        A parser instance.
    stream
        Either a stream object or a callable that produces one.  The
        stream object to read from; its 'read' method will be invoked.

        If a callable was provided, the 'close' method on its return
        value will be called to close the stream.
    """

    source = stream() if callable(stream) else stream
    try:
        text = source.read()
        stream_name = getattr(source, 'name', None)
        try:
            result = parser(text)
        except ECMASyntaxError as e:
            error_name = repr_compat(stream_name or source)
            raise type(e)('%s in %s' % (str(e), error_name))
    finally:
        if callable(stream):
            source.close()

    result.sourcepath = stream_name
    return result
예제 #6
0
 def t_BROKEN_STRING(self, token):
     # calculate colno for current token colno before...
     colno = self._get_colno(token)
     # updating the newline indexes for the error reporting for raw
     # lexpos
     self._update_newline_idx(token)
     # probe for the next values (which no valid rules will match)
     failure = self.lexer.lexdata[self.lexer.lexpos:self.lexer.lexpos + 2]
     if failure and failure[0] == '\\':
         type_ = {'x': 'hexadecimal', 'u': 'unicode'}[failure[1]]
         seq = re.match(r'\\[xu][0-9-a-f-A-F]*',
                        self.lexer.lexdata[self.lexer.lexpos:]).group()
         raise ECMASyntaxError("Invalid %s escape sequence '%s' at %s:%s" %
                               (type_, seq, self.lineno,
                                self._get_colno_lexpos(self.lexer.lexpos)))
     tl = 16  # truncate length
     raise ECMASyntaxError(
         'Unterminated string literal %s at %s:%s' %
         (repr_compat(token.value[:tl].strip() +
                      (token.value[tl:] and '...')), token.lineno, colno))
예제 #7
0
    def assemble(self, spec):
        """
        Assemble the library by compiling everything and generate the
        required files for the final bundling.
        """
        def generate_alias(prefix):
            alias = {}
            key = prefix + self.targetpath_suffix
            for modname, target in spec.get(key, {}).items():
                # the alias must point to the full path.
                alias[modname] = join(spec[BUILD_DIR], *target.split('/'))
            return alias

        # the build config is the file that will be passed to webpack for
        # building the final bundle.
        webpack_config = WebpackConfig({
            'mode':
            spec.get(WEBPACK_MODE, DEFAULT_WEBPACK_MODE),
            'devtool':
            spec.get(WEBPACK_DEVTOOL, DEFAULT_WEBPACK_DEVTOOL),
            'output': {
                'path': dirname(spec[EXPORT_TARGET]),
                'filename': basename(spec[EXPORT_TARGET]),
                # TODO determine if publicPath is needed.

                # TODO these are using magic values.  The library target
                # should be configured, along with umdNamedDefine also
                # when the way to expose the relevant options as proper
                # sets are determined.
                'libraryTarget': 'umd',
                'umdNamedDefine': True,
            },
            'resolve': {},
            # TODO should omit this if no alias are found
            'resolveLoader': {
                'alias': spec.get(WEBPACK_RESOLVELOADER_ALIAS, {}),
            },
            'externals':
            spec.get(WEBPACK_EXTERNALS, {}),
            'module': {},
        })
        if spec.get(WEBPACK_OPTIMIZE_MINIMIZE):
            webpack_config['optimization'] = {'minimize': True}
        if WEBPACK_OUTPUT_LIBRARY in spec:
            webpack_config['output']['library'] = spec[WEBPACK_OUTPUT_LIBRARY]

        version = get_bin_version(
            spec[self.webpack_bin_key],
            kw={
                'env':
                webpack_env(pathsep.join(self.find_node_modules_basedir())),
            })

        logger.debug("found webpack at '%s' to be version '%s'",
                     spec[self.webpack_bin_key], version)
        webpack_config['__webpack_target__'] = version

        # set up alias lookup mapping.
        webpack_config['resolve']['alias'] = alias = {}

        # generate the aliases - yes, we include the bundled sources to
        # be explicit as there are cases where an alternative bundle may
        # be specified using optional advices.
        main_prefixes = (
            'transpiled',
            'bundled',
        )
        source_alias = {}
        for prefix in main_prefixes:
            source_alias.update(generate_alias(prefix))

        # Do the same for loaders, but keep it in a separate mapping for
        # now.
        other_prefixes = ('loaderplugins', )
        other_alias = {}
        for prefix in other_prefixes:
            other_alias.update(generate_alias(prefix))

        # It is assumed that if WEBPACK_ENTRY_POINT is defined, it will
        # resolve into a target through the generated alias mapping.
        # Otherwise, assume one of the default calmjs style exports.
        if (spec.get(WEBPACK_ENTRY_POINT,
                     DEFAULT_BOOTSTRAP_EXPORT) == DEFAULT_BOOTSTRAP_EXPORT):
            # now resolve whether the webpack.externals has been defined
            # in a manner that requires the complete lookup module
            logger.info("spec webpack_entry_point defined to be '%s'",
                        DEFAULT_BOOTSTRAP_EXPORT)
            if (webpack_config['externals'].get(DEFAULT_BOOTSTRAP_EXPORT) ==
                    DEFAULT_BOOTSTRAP_EXPORT_CONFIG):
                logger.info(
                    "webpack.externals defined '%s' with value that enables "
                    "the calmjs webpack bootstrap module; generating module "
                    "with the complete bootstrap template",
                    DEFAULT_BOOTSTRAP_EXPORT)
                # assign the internal loader to the alias
                # TODO check that the default loader not being passed
                # through check_all_alias_declared is not an issue.
                alias[DEFAULT_CALMJS_EXPORT_NAME] = self.write_lookup_module(
                    spec, _DEFAULT_LOADER_FILENAME,
                    *_WEBPACK_CALMJS_MODULE_LOADER_TEMPLATE)
                # the bootstrap module will be the entry point in this
                # case.
                webpack_config['entry'] = self.write_bootstrap_module(spec)

                if (spec.get(WEBPACK_OUTPUT_LIBRARY) !=
                        DEFAULT_BOOTSTRAP_EXPORT):
                    # a simple warning will do, as this may only be an
                    # inconvenience.
                    logger.warning(
                        "exporting complete calmjs bootstrap module with "
                        "webpack.output.library as '%s' (expected '%s')",
                        spec.get(WEBPACK_OUTPUT_LIBRARY),
                        DEFAULT_BOOTSTRAP_EXPORT,
                    )
            else:
                logger.info(
                    "webpack.externals does not have '%s' defined for "
                    "the complete calmjs webpack bootstrap module; "
                    "generating simple export of modules",
                    DEFAULT_BOOTSTRAP_EXPORT)

                # one key bit: to avoid the default webpack behavior of
                # potentially overriding values on the root node (say,
                # there exists window.modules already by some other
                # package), only use the template that exports to module
                # if that is defined; otherwise simply use the load only
                # template that will just require the selected modules.
                if spec.get(WEBPACK_OUTPUT_LIBRARY):
                    template = _WEBPACK_ENTRY_CALMJS_EXPORT_ONLY_TEMPLATE
                else:
                    template = _WEBPACK_ENTRY_CALMJS_LOAD_ONLY_TEMPLATE

                # the resulting file is the entry point.
                webpack_config['entry'] = self.write_lookup_module(
                    spec, _DEFAULT_BOOTSTRAP_FILENAME, *template)
                if (spec.get(WEBPACK_OUTPUT_LIBRARY) ==
                        DEFAULT_BOOTSTRAP_EXPORT):
                    logger.critical(
                        "cowardly aborting export to webpack.output.library "
                        "as '%s' without the complete bootstrap; generating "
                        "module with export only template",
                        DEFAULT_BOOTSTRAP_EXPORT,
                    )
                    raise ValueError(
                        "aborting export of webpack.output.library as '%s' "
                        "with incomplete settings and bootstrap module" %
                        DEFAULT_BOOTSTRAP_EXPORT)
        else:
            # need to manually resolve the entry
            # if externals has been defined, use the complete lookup module
            # otherwise, use the simplified version.
            wp_ep = spec[WEBPACK_ENTRY_POINT]
            if wp_ep not in source_alias:
                msg = "'%s' not found in the source alias map" % wp_ep
                logger.error(msg)
                logger.info(
                    'source alias map {alias: targetpath}: %s',
                    json_dumps(source_alias),
                )
                raise ToolchainAbort(msg)
            webpack_config['entry'] = source_alias[wp_ep]

        # merge all aliases for writing of configuration file
        alias.update(source_alias)
        alias.update(other_alias)
        # record the webpack config to the spec
        spec[WEBPACK_CONFIG] = webpack_config

        if spec.get(VERIFY_IMPORTS, True):
            missing = self.check_all_alias_declared(
                source_alias,
                partial(
                    check_name_declared,
                    webpack_config['resolve']['alias'],
                    webpack_config['resolveLoader']['alias'],
                    webpack_config['externals'],
                    spec.get(CALMJS_LOADERPLUGIN_REGISTRY),
                ))
            if missing:
                logger.warning(
                    "source file(s) referenced modules that are not in alias "
                    "or externals: %s",
                    ', '.join(sorted(repr_compat(m) for m in missing)))

        update_spec_webpack_loaders_modules(spec, alias)
        webpack_config['module']['rules'] = spec.get(WEBPACK_MODULE_RULES, [])

        # write the configuration file, after everything is checked.
        self.write_webpack_config(spec, webpack_config)
예제 #8
0
 def __repr__(self):
     return str('<%s value=%s>' %
                (type(self).__name__, repr_compat(self.value)))