New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using via Stylus CLI? #1

Closed
igregson opened this Issue Jun 17, 2015 · 11 comments

Comments

Projects
None yet
4 participants
@igregson

igregson commented Jun 17, 2015

First off, thanks for helping bring postcss goodness to Stylus folks :)

Question: Is it possible to use this via the Stylus CLI?

Context/Attempts:

In the spirit of http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/ I've been using NPM for most of my build needs. Using plugins via their CLI works just fine. It's possible to pass options to the plugins via the the undocumented --with flag (issue here stylus/stylus#397 and here http://programming.nullanswer.com/question/26995195), which is the following from the Stylus CLI file:

      if ('--with' == args[0]) {
        args.shift();
        options = args.shift();
        if (!options) throw new Error('--with <options> required');
        options = eval('(' + options + ')');
      }

From the Stylus docs:

So, what we need to do is use the --use, or -u flag. It expects a path to a node module (with or without the .js extension). This require()s the module, expecting a function to be exported as module.exports, which then calls style.use(fn()) to expose the plugin (defining its js functions, etc.).

Below is my first attempt, using the config NPM variable as a way of passing an array via the --with flag.

{
  "config": {
    "postcssPlugins": [
      "postcss-cssstats",
      "lost"
    ]
  },
  "scripts": {
    "css:w": "stylus --compress --use poststylus --with {$npm_package_config_postcssPlugins} --sourcemap --sourcemap-base ./src-css/ --watch ./src-css/ --out ./app/css",
  },
}

This seems to be transferring the postcssPlugins array as an object, which throws an error because Stylus simply tries to require the object:

              throw err;
                    ^
ParseError: node_modules/lost/lost.js:5:69
   1| /**
   2|  * Module dependencies
   3|  */
   4| var postcss = require('postcss'),
   5|     assign = require('object-assign');
--------------------------------------------------------------------------^
   6| 
   7| 
   8| /**

expected "indent", got ";"

Then I tried this different things along this approach:

-with '{opt:'lost'}'

Which throws an error at the eval Stylus step of the Stylus CLI:

ReferenceError: lost is not defined
    at Object.eval (eval at <anonymous> 

I've tried lots of other things but am generally unsure of whether this is possible or not. And, if it isn't, I'm not sure which to try to fork/PR - Stylus' CLI or poststylus.

The Stylus CLI is generally awesome. I've never been happier (I'm a recent Stylus convert from Sass, should've made the switch sooner!) Now to just figure out how to integrate postcss plugins into this setup (which will hopefully grow in popularity - small yet powerful - incredibly manageable)...

@igregson

This comment has been minimized.

Show comment
Hide comment
@igregson

igregson Jun 17, 2015

Also, while a purely CLI-based approach is preferred, using an external JS file would also work. This approach would also allow for this https://github.com/seaneking/poststylus#custom-postcss

I've tried, but can't seem to wire it up correctly. Generally, I don't think this is possible (unless this functionality is add into Stylus' CLI)... is this correct?

igregson commented Jun 17, 2015

Also, while a purely CLI-based approach is preferred, using an external JS file would also work. This approach would also allow for this https://github.com/seaneking/poststylus#custom-postcss

I've tried, but can't seem to wire it up correctly. Generally, I don't think this is possible (unless this functionality is add into Stylus' CLI)... is this correct?

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jun 17, 2015

Owner

As @Panya mentioned in the issue you opened on the stylus repo, it seems --with just takes a string and eval()s it before passing that to the plugin. The errors you were seeing were because a) you're not defining 'postcss-stats', etc. anywhere (ie: have to require() the module - think of it like passing plugins to a plugin), and b) your npm script is passing an object to --with, whereas poststylus takes an array (of plugins).

So the snippet posted in the other issue thread is technically correct:

stylus --use ./node_modules/poststylus --with '[require("postcss-position")]' style.styl

However that require is going to be evaluated from the location of your stylus binary (/usr/lib if you're on *nix), rather than your current project directory. So it will complain that it can't find the module.

Can't think of a clean solution to this off the top of my head now. But I'll do a bit of digging.

Owner

seaneking commented Jun 17, 2015

As @Panya mentioned in the issue you opened on the stylus repo, it seems --with just takes a string and eval()s it before passing that to the plugin. The errors you were seeing were because a) you're not defining 'postcss-stats', etc. anywhere (ie: have to require() the module - think of it like passing plugins to a plugin), and b) your npm script is passing an object to --with, whereas poststylus takes an array (of plugins).

So the snippet posted in the other issue thread is technically correct:

stylus --use ./node_modules/poststylus --with '[require("postcss-position")]' style.styl

However that require is going to be evaluated from the location of your stylus binary (/usr/lib if you're on *nix), rather than your current project directory. So it will complain that it can't find the module.

Can't think of a clean solution to this off the top of my head now. But I'll do a bit of digging.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jun 17, 2015

Owner

Okay so I can't think of a clean way around this without significantly changing how PostStylus works. You can just install all your postcss plugins globally if you don't mind polluting your global npm, but that seems a little extreme just to get around using js / a build tool.

If you don't mind using js for this, then you could just whip together a little node script for npm to run, something along the lines of:

var stylus = require('stylus'),
    fs = require('fs')
    poststylus = require('poststylus');

var postcssPlugins = [
  // postcss plugins
];

var input = process.argv[2]
var output = process.argv[3]
var file = fs.readFileSync(input, 'utf8');

stylus(file)
  .use(poststylus(postcssPlugins))
  .render(function(err, css){
    fs.writeFile(output, css)
  });

which you'd run (or get npm to run) with node process-stylus.js 'style.style' 'style.css' (assuming the script is called process.stylus.js).

Anyway, this is more a limitation of the CLI than with PostStylus, so I'm closing it for now. If there's sufficient demand for a dedicated CLI helper for PostStylus I'll look into doing that in the future. For now I'd recommend either using a build tool like Gulp or a js script with npm.

Owner

seaneking commented Jun 17, 2015

Okay so I can't think of a clean way around this without significantly changing how PostStylus works. You can just install all your postcss plugins globally if you don't mind polluting your global npm, but that seems a little extreme just to get around using js / a build tool.

If you don't mind using js for this, then you could just whip together a little node script for npm to run, something along the lines of:

var stylus = require('stylus'),
    fs = require('fs')
    poststylus = require('poststylus');

var postcssPlugins = [
  // postcss plugins
];

var input = process.argv[2]
var output = process.argv[3]
var file = fs.readFileSync(input, 'utf8');

stylus(file)
  .use(poststylus(postcssPlugins))
  .render(function(err, css){
    fs.writeFile(output, css)
  });

which you'd run (or get npm to run) with node process-stylus.js 'style.style' 'style.css' (assuming the script is called process.stylus.js).

Anyway, this is more a limitation of the CLI than with PostStylus, so I'm closing it for now. If there's sufficient demand for a dedicated CLI helper for PostStylus I'll look into doing that in the future. For now I'd recommend either using a build tool like Gulp or a js script with npm.

@seaneking seaneking closed this Jun 17, 2015

@igregson

This comment has been minimized.

Show comment
Hide comment
@igregson

igregson Jun 17, 2015

Great. Many thanks for the follow-up on this. I've been trying to go the global route (which I agree isn't ideal) but have been having problems with Stylus not being able to locate postcss plugins (if everything is simply installed globally it should work, right?).

Anyways, thanks for sharing the above approach to calling stylus from NPM. Any thoughts on making the above support directories? If not, no worries.

igregson commented Jun 17, 2015

Great. Many thanks for the follow-up on this. I've been trying to go the global route (which I agree isn't ideal) but have been having problems with Stylus not being able to locate postcss plugins (if everything is simply installed globally it should work, right?).

Anyways, thanks for sharing the above approach to calling stylus from NPM. Any thoughts on making the above support directories? If not, no worries.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jun 17, 2015

Owner

Hmm not sure why it still wouldn't be able to find the postcss plugins if they're installed globally. I'd have to dig around the stylus executable. But as you say installing everything in the global scope isn't really a workable solution (dependency management across projects would be impossible).

For anything more complex than that little node script I'd seriously consider just using Gulp. Even if you just write a single gulp task to pass to your npm-script workflow. Check out the example in the README, for multiple files just sub style.styl with a glob (eg: styles/*.styl). Then the npm script can just run gulp.

Owner

seaneking commented Jun 17, 2015

Hmm not sure why it still wouldn't be able to find the postcss plugins if they're installed globally. I'd have to dig around the stylus executable. But as you say installing everything in the global scope isn't really a workable solution (dependency management across projects would be impossible).

For anything more complex than that little node script I'd seriously consider just using Gulp. Even if you just write a single gulp task to pass to your npm-script workflow. Check out the example in the README, for multiple files just sub style.styl with a glob (eg: styles/*.styl). Then the npm script can just run gulp.

@igregson

This comment has been minimized.

Show comment
Hide comment
@igregson

igregson Jun 17, 2015

Figured it out... things were working just fine (just not me!). I didn't realize that the postcss package (lost, in this case) was already available and I was trying to @import it. Not being familiar with lost yes, I had quotes around my column fractions lost-column: '4/12', thinking that Stylus needed it that way (lest it try and make a division operation). So I though lost wasn't working when it actually was :)

Now everything is working great. For the time being I'm fine with the global approach. I've used Gulp a lot in other projects... certainly a nice api and fine route to go, only I'm trying to minimize complexity as much as possible. And it's pretty remarkable how much can be done with only a few NPM script lines :)

Thanks again, especially for poststylus :)

igregson commented Jun 17, 2015

Figured it out... things were working just fine (just not me!). I didn't realize that the postcss package (lost, in this case) was already available and I was trying to @import it. Not being familiar with lost yes, I had quotes around my column fractions lost-column: '4/12', thinking that Stylus needed it that way (lest it try and make a division operation). So I though lost wasn't working when it actually was :)

Now everything is working great. For the time being I'm fine with the global approach. I've used Gulp a lot in other projects... certainly a nice api and fine route to go, only I'm trying to minimize complexity as much as possible. And it's pretty remarkable how much can be done with only a few NPM script lines :)

Thanks again, especially for poststylus :)

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jun 17, 2015

Owner

no worries, glad it's all sorted ;-)

Owner

seaneking commented Jun 17, 2015

no worries, glad it's all sorted ;-)

@bedeoverend

This comment has been minimized.

Show comment
Hide comment
@bedeoverend

bedeoverend Jun 18, 2015

Contributor

@igregson there is a way to do this using pure CLI, but it's not that pretty, and in general, it's working with a lot of hacks, so honestly it'd probably be better to use a task runner until these tools are going to work.

Basically there's a bunch of problems:

  • Stylus is being run from global, so when you're giving it plugins with --use it either wants a module name that's been installed globally, or a path to the module. Therefore use --use ./node_modules/poststylus sorry, didn't realise @seaneking had mentioned that earlier
  • Second issue, poststylus takes plugins as functions. You're passing an object of names of modules into with but stylus is going to eval that and pass it directly into the plugin you're using, in this case, poststylus, which infact takes an array of plugins, so you instead need to pass like so: --with [...] sorry, missed that @seaneking had already covered that earlier
  • Third issue, NPM config doesn't like arrays, so to reference the variables in an array inside the package config, you need to use this syntax: $npm_package_config_postcssPlugins_0 where 0 is the index in the array (looks like NPM just flattens the JSON into snake case, keys for objects and indexes - essentially keys in JS - for arrays), AFAIK, you can't access the entire array. Instead, you should store it in a string e.g. "require('postcss-position')(), require('lost')()"
  • Lastly, stylus is going to try require those modules from it's own directory, same problem as problem 1, so we need to set the env variable NODE_PATH before hand to PWD i.e. NODE_PATH=$PWD

Finally, this should work:

{
  "config": {
    "postcssPlugins": "require('postcss-cssstats')(), require('lost')()"
  },
  "scripts": {
    "css:w": "NODE_PATH=$PWD && stylus --compress --use poststylus --with \"[$npm_package_config_plugins]\" --sourcemap --sourcemap-base ./src-css/ --watch ./src-css/ --out ./app/css",
  },
}

As I said, it's messy but it uses NPM alone and doesn't use global modules.

Contributor

bedeoverend commented Jun 18, 2015

@igregson there is a way to do this using pure CLI, but it's not that pretty, and in general, it's working with a lot of hacks, so honestly it'd probably be better to use a task runner until these tools are going to work.

Basically there's a bunch of problems:

  • Stylus is being run from global, so when you're giving it plugins with --use it either wants a module name that's been installed globally, or a path to the module. Therefore use --use ./node_modules/poststylus sorry, didn't realise @seaneking had mentioned that earlier
  • Second issue, poststylus takes plugins as functions. You're passing an object of names of modules into with but stylus is going to eval that and pass it directly into the plugin you're using, in this case, poststylus, which infact takes an array of plugins, so you instead need to pass like so: --with [...] sorry, missed that @seaneking had already covered that earlier
  • Third issue, NPM config doesn't like arrays, so to reference the variables in an array inside the package config, you need to use this syntax: $npm_package_config_postcssPlugins_0 where 0 is the index in the array (looks like NPM just flattens the JSON into snake case, keys for objects and indexes - essentially keys in JS - for arrays), AFAIK, you can't access the entire array. Instead, you should store it in a string e.g. "require('postcss-position')(), require('lost')()"
  • Lastly, stylus is going to try require those modules from it's own directory, same problem as problem 1, so we need to set the env variable NODE_PATH before hand to PWD i.e. NODE_PATH=$PWD

Finally, this should work:

{
  "config": {
    "postcssPlugins": "require('postcss-cssstats')(), require('lost')()"
  },
  "scripts": {
    "css:w": "NODE_PATH=$PWD && stylus --compress --use poststylus --with \"[$npm_package_config_plugins]\" --sourcemap --sourcemap-base ./src-css/ --watch ./src-css/ --out ./app/css",
  },
}

As I said, it's messy but it uses NPM alone and doesn't use global modules.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jun 18, 2015

Owner

I'm reopening this until #2 is merged and we can write all this up into the README in case anyone else comes looking for a CLI solution.

Though really stylus' whack --with implementation needs to be patched.

Owner

seaneking commented Jun 18, 2015

I'm reopening this until #2 is merged and we can write all this up into the README in case anyone else comes looking for a CLI solution.

Though really stylus' whack --with implementation needs to be patched.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jun 18, 2015

Owner

And thanks @bedeoverend for the PWD hack, not pretty but better than using global deps.

@igregson if you wanted to do this on the CLI directly you'd do something like

stylus --use ./node_modules/poststylus --with "[require('${PWD}/node_modules/postcss-position/')()]" --out test.css < test.styl
Owner

seaneking commented Jun 18, 2015

And thanks @bedeoverend for the PWD hack, not pretty but better than using global deps.

@igregson if you wanted to do this on the CLI directly you'd do something like

stylus --use ./node_modules/poststylus --with "[require('${PWD}/node_modules/postcss-position/')()]" --out test.css < test.styl

@seaneking seaneking closed this in 5320d33 Jun 18, 2015

@adarrra

This comment has been minimized.

Show comment
Hide comment
@adarrra

adarrra Apr 25, 2016

hi! sorry for maybe silly question but I don't understand can I use options.json file with stylus CLI? For example I want
stylus --use ./node_modules/poststylus --with \"['autoprefixer', 'postcss-csso']\"
and for each plugin I need some config. In postccs CLI is --config flag.

Or with stylus CLI I can only use this:
[require('${PWD}/node_modules/autoprefixer/')({browsers: ['ie 8']}), require('${PWD}/node_modules/postcss-csso/')({restructure: false})]
?

adarrra commented Apr 25, 2016

hi! sorry for maybe silly question but I don't understand can I use options.json file with stylus CLI? For example I want
stylus --use ./node_modules/poststylus --with \"['autoprefixer', 'postcss-csso']\"
and for each plugin I need some config. In postccs CLI is --config flag.

Or with stylus CLI I can only use this:
[require('${PWD}/node_modules/autoprefixer/')({browsers: ['ie 8']}), require('${PWD}/node_modules/postcss-csso/')({restructure: false})]
?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment