예제 #1
0
class CompassConnectorFilter(Filter):
    name = 'compassconnector'
    max_debug_level = None

    options = {
        'compass': ('binary', 'COMPASS_BIN'),
        'plugins': option('COMPASS_PLUGINS', type=list),
        'vendor_path': ('relative path', 'VENDOR_PATH'),
        'imports': option('COMPASS_IMPORTS', type=list),
    }

    depends = None

    def find_dependencies(self):
        return self.depends

    def unique(self):
        hash_func(self.plugins)

    def input(self, in_, out, **kwargs):
        h = connector.Handler(self.env, in_, out,
                              self.plugins if self.plugins else {},
                              self.imports if self.imports else [],
                              kwargs["source"])
        h.vendor_path = self.vendor_path
        if not self.compass:
            raise FilterError("Compass bin path is not set")
        h.start(self.compass)

        self.depends = h.deps
예제 #2
0
class Browserify(ExternalTool):
    """Use Browserify to bundle assets.

    Requires the Browserify executable to be available externally. You can
    install it using `Node Package Manager <http://npmjs.org/>`_::

        $ npm install -g browserify

    Supported configuration options:

    BROWSERIFY_BIN
        The path to the Browserify binary. If not set, assumes ``browserify``
        is in the system path.

    BROWSERIFY_TRANSFORMS
        A list of Browserify transforms to use. Each transform will be included
        via Browserify's command-line ``--transform`` argument. If you need
        to pass arguments to a transform, use a list:
        ``['babelify', '--stage', '0']``

    BROWSERIFY_EXTRA_ARGS
        A list of any additional command-line arguments.

    """

    name = 'browserify'
    max_debug_level = None
    options = {
        'binary': 'BROWSERIFY_BIN',
        'transforms': option('BROWSERIFY_TRANSFORMS', type=list),
        'extra_args': option('BROWSERIFY_EXTRA_ARGS', type=list)
    }

    def input(self, infile, outfile, **kwargs):
        args = [self.binary or 'browserify']

        for transform in self.transforms or []:
            if isinstance(transform, (list, tuple)):
                args.extend(('--transform', '['))
                args.extend(transform)
                args.append(']')
            else:
                args.extend(('--transform', transform))

        if self.extra_args:
            args.extend(self.extra_args)

        args.append(kwargs['source_path'])

        self.subprocess(args, outfile, infile)
예제 #3
0
class Rollup(ExternalTool):
    """Use Rollup to bundle assets.

    Requires the Rollup executable to be available externally. You can
    install it using `Node Package Manager <http://npmjs.org/>`_::

        $ npm install -g rollup

    Supported configuration options:

    ROLLUP_BIN
        The path to the Rollup binary. If not set, assumes ``rollup``
        is in the system path.

    ROLLUP_EXTRA_ARGS
        A list of any additional command-line arguments.

    """

    name = 'rollup'
    max_debug_level = None
    options = {
        'binary': 'ROLLUP_BIN',
        'extra_args': option('ROLLUP_EXTRA_ARGS', type=list)
    }

    def input(self, infile, outfile, **kwargs):
        args = [self.binary or 'rollup']

        if self.extra_args:
            args.extend(self.extra_args)

        args.append(kwargs['source_path'])

        self.subprocess(args, outfile, infile)
예제 #4
0
파일: stylus.py 프로젝트: tilgovi/webassets
class Stylus(Filter):
    """Converts `Stylus <http://learnboost.github.com/stylus/>`_ markup to CSS.

    Requires the Stylus executable to be available externally. You can install
    it using the `Node Package Manager <http://npmjs.org/>`_::

        $ npm install stylus

    Supported configuration options:

    STYLUS_BIN
        The path to the Stylus binary. If not set, assumes ``stylus`` is in the
        system path.

    STYLUS_PLUGINS
        A Python list of Stylus plugins to use. Each plugin will be included
        via Stylus's command-line ``--use`` argument.

    STYLUS_EXTRA_ARGS
        A Python list of any additional command-line arguments.
    """

    name = 'stylus'
    options = {
        'stylus': 'STYLUS_BIN',
        'plugins': option('STYLUS_PLUGINS', type=list),
        'extra_args': option('STYLUS_EXTRA_ARGS', type=list),
    }
    max_debug_level = None

    def input(self, _in, out, **kwargs):
        args = [self.stylus or 'stylus']
        for plugin in self.plugins or []:
            args.extend(('--use', plugin))
        if self.extra_args:
            args.extend(self.extra_args)

        PIPE = subprocess.PIPE
        proc = subprocess.Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        stdout, stderr = proc.communicate(_in.read())

        if proc.returncode != 0:
            raise FilterError('%s: subprocess had error: stderr=%s, code=%s',
                              self.name, stderr, proc.returncode)
        out.write(stdout)
예제 #5
0
파일: stylus.py 프로젝트: Rehy0810/database
class Stylus(ExternalTool):
    """Converts `Stylus <http://learnboost.github.com/stylus/>`_ markup to CSS.

    Requires the Stylus executable to be available externally. You can install
    it using the `Node Package Manager <http://npmjs.org/>`_::

        $ npm install -g stylus

    Supported configuration options:

    STYLUS_BIN
        The path to the Stylus binary. If not set, assumes ``stylus`` is in the
        system path.

    STYLUS_PLUGINS
        A Python list of Stylus plugins to use. Each plugin will be included
        via Stylus's command-line ``--use`` argument.

    STYLUS_EXTRA_ARGS
        A Python list of any additional command-line arguments.
        
    STYLUS_EXTRA_PATHS
        A Python list of any additional import paths.
    """

    name = 'stylus'
    options = {
        'stylus': 'STYLUS_BIN',
        'plugins': option('STYLUS_PLUGINS', type=list),
        'extra_args': option('STYLUS_EXTRA_ARGS', type=list),
        'extra_paths': option('STYLUS_EXTRA_PATHS', type=list),
    }
    max_debug_level = None

    def input(self, _in, out, **kwargs):
        args = [self.stylus or 'stylus']
        source_dir = os.path.dirname(kwargs['source_path'])
        paths = [source_dir] + (self.extra_paths or [])
        for path in paths:
            args.extend(('--include', path))
        for plugin in self.plugins or []:
            args.extend(('--use', plugin))
        if self.extra_args:
            args.extend(self.extra_args)
        self.subprocess(args, out, _in)
예제 #6
0
class CompileLess(Filter):
    name = 'lessc'
    max_debug_level = None
    options = {'less_includes': option('LIBSASS_INCLUDES', type=list)}

    def input(self, _in, out, **kw):
        options = {'paths': []}
        if self.less_includes:
            options['paths'].extend(self.less_includes)
        if 'source_path' in kw:
            options['paths'].append(os.path.dirname(kw['source_path']))

        src = dukpy.less_compile(_in.read(), options=options)
        out.write(src)
예제 #7
0
class Compass(Filter):
    """Converts `Compass <http://compass-style.org/>`_ .sass files to
    CSS.

    Requires at least version 0.10.

    To compile a standard Compass project, you only need to have
    to compile your main ``screen.sass``, ``print.sass`` and ``ie.sass``
    files. All the partials that you include will be handled by Compass.

    If you want to combine the filter with other CSS filters, make
    sure this one runs first.

    Supported configuration options:

    COMPASS_BIN
        The path to the Compass binary. If not set, the filter will
        try to run ``compass`` as if it's in the system path.

    COMPASS_PLUGINS
        Compass plugins to use. This is equivalent to the ``--require``
        command line option of the Compass. and expects a Python list
        object of Ruby libraries to load.

    COMPASS_CONFIG
        An optional dictionary of Compass `configuration options
        <http://compass-style.org/help/documentation/configuration-reference/>`_.
        The values are emitted as strings, and paths are relative to the
        Environment's ``directory`` by default; include a ``project_path``
        entry to override this.

        The ``sourcemap`` option has a caveat. A file called _.css.map is
        created by Compass in the tempdir (where _.scss is the original asset),
        which is then moved into the output_path directory. Since the tempdir
        is created one level down from the output path, the relative links in
        the sourcemap should correctly map. This file, however, will not be
        versioned, and thus this option should ideally only be used locally
        for development and not in production with a caching service as the
        _.css.map file will not be invalidated.
    """

    name = 'compass'
    max_debug_level = None
    options = {
        'compass': ('binary', 'COMPASS_BIN'),
        'plugins': option('COMPASS_PLUGINS', type=list),
        'config': 'COMPASS_CONFIG',
    }

    def open(self, out, source_path, **kw):
        """Compass currently doesn't take data from stdin, and doesn't allow
        us accessing the result from stdout either.

        Also, there's a bunch of other issues we need to work around:

         - compass doesn't support given an explict output file, only a
           "--css-dir" output directory.

           We have to "guess" the filename that will be created in that
           directory.

         - The output filename used is based on the input filename, and
           simply cutting of the length of the "sass_dir" (and changing
           the file extension). That is, compass expects the input
           filename to always be inside the "sass_dir" (which defaults to
           ./src), and if this is not the case, the output filename will
           be gibberish (missing characters in front). See:
           https://github.com/chriseppstein/compass/issues/304

           We fix this by setting the proper --sass-dir option.

         - Compass insists on creating a .sass-cache folder in the
           current working directory, and unlike the sass executable,
           there doesn't seem to be a way to disable it.

           The workaround is to set the working directory to our temp
           directory, so that the cache folder will be deleted at the end.
        """

        # Create temp folder one dir below output_path so sources in
        # sourcemap are correct. This will be in the project folder,
        # and as such, while exteremly unlikely, this could interfere
        # with existing files and directories.
        tempout_dir = path.normpath(
            path.join(path.dirname(kw['output_path']), '../')
        )
        tempout = tempfile.mkdtemp(dir=tempout_dir)
        # Temporarily move to "tempout", so .sass-cache will be created there
        old_wd = os.getcwd()
        os.chdir(tempout)
        try:
            # Make sure to use normpath() to not cause trouble with
            # compass' simplistic path handling, where it just assumes
            # source_path is within sassdir, and cuts off the length of
            # sassdir from the input file.
            sassdir = path.normpath(path.dirname(source_path))
            source_path = path.normpath(source_path)

            # Compass offers some helpers like image-url(), which need
            # information about the urls under which media files will be
            # available. This is hard for two reasons: First, the options in
            # question aren't supported on the command line, so we need to write
            # a temporary config file. Secondly, they assume defined and
            # separate directories for "images", "stylesheets" etc., something
            # webassets knows nothing of: we don't support the user defining
            # such directories. Because we traditionally had this
            # filter point all type-specific directories to the root media
            # directory, we will define the paths to match this. In other
            # words, in Compass, both inline-image("img/test.png) and
            # image-url("img/test.png") will find the same file, and assume it
            # to be {env.directory}/img/test.png.
            # However, this partly negates the purpose of an utility like
            # image-url() in the first place - you not having to hard code
            # the location of your images. So we allow direct modification of
            # the configuration file via the COMPASS_CONFIG setting (see
            # tickets #36 and #125).
            #
            # Note that there is also the --relative-assets option, which we
            # can't use because it calculates an actual relative path between
            # the image and the css output file, the latter being in a
            # temporary directory in our case.
            config = CompassConfig(
                project_path=self.ctx.directory,
                http_path=self.ctx.url,
                http_images_dir='',
                http_stylesheets_dir='',
                http_fonts_dir='',
                http_javascripts_dir='',
                images_dir='',
                output_style=':expanded',
            )
            # Update with the custom config dictionary, if any.
            if self.config:
                config.update(self.config)
            config_file = path.join(tempout, '.config.rb')
            f = open(config_file, 'w')
            try:
                f.write(config.to_string())
                f.flush()
            finally:
                f.close()

            command = [self.compass or 'compass', 'compile']
            for plugin in self.plugins or []:
                command.extend(('--require', plugin))
            command.extend(['--sass-dir', sassdir,
                            '--css-dir', tempout,
                            '--config', config_file,
                            '--quiet',
                            '--boring',
                            source_path])
            proc = subprocess.Popen(command,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,
                                    # shell: necessary on windows to execute
                                    # ruby files, but doesn't work on linux.
                                    shell=(os.name == 'nt'))
            stdout, stderr = proc.communicate()

            # compass seems to always write a utf8 header? to stderr, so
            # make sure to not fail just because there's something there.
            if proc.returncode != 0:
                raise FilterError(('compass: subprocess had error: stderr=%s, '+
                                   'stdout=%s, returncode=%s') % (
                                                stderr, stdout, proc.returncode))

            guessed_outputfilename = path.splitext(path.basename(source_path))[0]
            guessed_outputfilepath = path.join(tempout, guessed_outputfilename)
            output_file = open("%s.css" % guessed_outputfilepath, encoding='utf-8')
            if config.get('sourcemap'):
                sourcemap_file = open("%s.css.map" % guessed_outputfilepath)
                sourcemap_output_filepath = path.join(
                    path.dirname(kw['output_path']),
                    path.basename(sourcemap_file.name)
                )
                if not path.exists(path.dirname(sourcemap_output_filepath)):
                    os.mkdir(path.dirname(sourcemap_output_filepath))
                sourcemap_output_file = open(sourcemap_output_filepath, 'w')
                sourcemap_output_file.write(sourcemap_file.read())
                sourcemap_file.close()
            try:
                contents = output_file.read()
                out.write(contents)
            finally:
                output_file.close()
        finally:
            # Restore previous working dir
            os.chdir(old_wd)
            # Clean up the temp dir
            shutil.rmtree(tempout)
예제 #8
0
 def option__deepcopy__(value, memo):
     """Custom deepcopy implementation for ``option`` class."""
     return option(copy.deepcopy(value[0]), copy.deepcopy(value[1]), copy.deepcopy(value[2]))
예제 #9
0
class Compass(Filter):
    """Converts `Compass <http://compass-style.org/>`_ .sass files to
    CSS.

    Requires at least version 0.10.

    To compile a standard Compass project, you only need to have
    to compile your main ``screen.sass``, ``print.sass`` and ``ie.sass``
    files. All the partials that you include will be handled by Compass.

    If you want to combine the filter with other CSS filters, make
    sure this one runs first.

    Supported configuration options:

    COMPASS_BIN
        The path to the Compass binary. If not set, the filter will
        try to run ``compass`` as if it's in the system path.

    COMPASS_PLUGINS
        Compass plugins to use. This is equivalent to the ``--require``
        command line option of the Compass. and expects a Python list
        object of Ruby libraries to load.
    """

    name = 'compass'
    max_debug_level = None
    options = {
        'compass': ('binary', 'COMPASS_BIN'),
        'plugins': option('COMPASS_PLUGINS', type=list)
    }

    def open(self, out, source_path, **kw):
        """Compass currently doesn't take data from stdin, and doesn't allow
        us accessing the result from stdout either.

        Also, there's a bunch of other issues we need to work around:

         - compass doesn't support given an explict output file, only a
           "--css-dir" output directory.

           We have to "guess" the filename that will be created in that
           directory.

         - The output filename used is based on the input filename, and
           simply cutting of the length of the "sass_dir" (and changing
           the file extension). That is, compass expects the input
           filename to always be inside the "sass_dir" (which defaults to
           ./src), and if this is not the case, the output filename will
           be gibberish (missing characters in front). See:
           https://github.com/chriseppstein/compass/issues/304

           We fix this by setting the proper --sass-dir option.

         - Compass insists on creating a .sass-cache folder in the
           current working directory, and unlike the sass executable,
           there doesn't seem to be a way to disable it.

           The workaround is to set the working directory to our temp
           directory, so that the cache folder will be deleted at the end.
        """

        tempout = tempfile.mkdtemp()
        # Temporarily move to "tempout", so .sass-cache will be created there
        old_wd = os.getcwdu()
        os.chdir(tempout)
        try:
            # Make sure to use normpath() to not cause trouble with
            # compass' simplistic path handling, where it just assumes
            # source_path is within sassdir, and cuts off the length of
            # sassdir from the input file.
            sassdir = path.normpath(path.dirname(source_path))
            source_path = path.normpath(source_path)

            # Compass offers some helpers like image-url(), which need
            # information about the urls under which media files will be
            # available. This is hard for two reasons: First, the options in
            # question aren't supported on the command line, so we need to write
            # a temporary config file. Secondly, the assume a defined and
            # separate directories for "images", "stylesheets" etc., something
            # webassets knows nothing of: we don't support the user defining
            # something such directories. Because we traditionally had this
            # filter point all type-specific directories to the root media
            # directory, we will define the paths to match this. In other
            # words, in Compass, both inline-image("img/test.png) and
            # image-url("img/test.png") will find the same file, and assume it
            # to be {env.directory}/img/test.png.
            # However, this partly negates the purpose of an utiility like
            # image-url() in the first place - you not having to hard code
            # the location of your images. So a possiblity for the future
            # might be adding options that allow changing this behavior (see
            # ticket #36).
            #
            # Note that is also the --relative-assets option, which we can't
            # use because it calculates an actual relative path between the
            # image and the css output file, the latter being in a temporary
            # directory in our case.
            config_file = path.join(tempout, '.config.rb')
            f = open(config_file, 'w')
            try:
                f.write("""
http_path = "%s"
http_images_dir = ""
http_stylesheets_dir = ""
http_fonts_dir = ""
http_javascripts_dir = ""
    """ % self.env.url)
                f.flush()
            finally:
                f.close()

            command = [self.compass or 'compass', 'compile']
            for plugin in self.plugins or []:
                command.extend(('--require', plugin))
            command.extend([
                '--sass-dir', sassdir, '--css-dir', tempout, '--image-dir',
                self.env.directory, '--config', config_file, '--quiet',
                '--boring', '--output-style', 'expanded', source_path
            ])

            proc = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                # shell: necessary on windows to execute
                # ruby files, but doesn't work on linux.
                shell=(os.name == 'nt'))
            stdout, stderr = proc.communicate()

            # compass seems to always write a utf8 header? to stderr, so
            # make sure to not fail just because there's something there.
            if proc.returncode != 0:
                raise FilterError(
                    ('compass: subprocess had error: stderr=%s, ' +
                     'stdout=%s, returncode=%s') %
                    (stderr, stdout, proc.returncode))


            guessed_outputfile = \
                path.join(tempout, path.splitext(path.basename(source_path))[0])
            f = open("%s.css" % guessed_outputfile)
            try:
                out.write(f.read())
            finally:
                f.close()
        finally:
            # Restore previous working dir
            os.chdir(old_wd)
            # Clean up the temp dir
            shutil.rmtree(tempout)
예제 #10
0
 def option__deepcopy__(value, memo):
     """Custom deepcopy implementation for ``option`` class."""
     return option(copy.deepcopy(value[0]), copy.deepcopy(value[1]),
                   copy.deepcopy(value[2]))