Request for Autoloading Plugins as a Core Functionality #813

Open
michael-ciniawsky opened this Issue May 25, 2016 · 51 comments

Projects

None yet

5 participants

@michael-ciniawsky
michael-ciniawsky commented May 25, 2016 edited

Hi, is there some interest in making it possible for postcss to auto-load plugins based on a config obj either inline, from a separate js | json file or a section in pkg.json (e.g pkg.postcss)?

The plugin names are written without the postcss- prefix and hyphen delimited names should be written in camelCase. The postition in the plugins array is determined by the plugins[i].key e.g =>
[plugin1(), plugin2(), pluginName3()].
In other words it matters in which order you declare your options ;).

Inline (not that useful, but for demonstration)

 const postcss = require('postcss')

// Same as 
// const plugin1 = require('poscss-plugin1')({})
// const plugin2 = require('poscss-plugin2')({})
// const plugin3 = require('poscss-plugin-name3')({})
const plugins = {
 plugin1: {/* options */},
 plugin2: {/* options */},
 pluginName3: {/* options */}
}

postcss(plugins).process...

plugins.js

module.exports = { //order
  plugin1: {},  //1.
  plugin2: {}   //2.
  pluginName3: {}   //3.
}

plugins.json

{
  "plugin1": {},
  "plugin2": {} ,
  "pluginName3": {}
}

package.json

"postcss": {
  "plugin1": {},
  "plugin2": {}  
}
// from 
postcss(require('postcss-loads-plugins')('./plugins')).process...
// to
postcss('./plugins').process

The way it which it's handled at the moment is not affected in anyways. It's just an attempt to make it possible to pass an obj or string directly to the loader API and then plugin requires and array scaffolding is handled in core.

@ai
Member
ai commented May 25, 2016

I agree that we need common config. So we will have autoload for JSON configs abd require for JS.

@ai
Member
ai commented May 25, 2016

Do you want to organize all discussion for this task, find best API and implement it? I will help and guide you.

@michael-ciniawsky

@ai abd require JS === no js files as conf? :)

@ai
Member
ai commented May 25, 2016

Oops 😊 "and require() in JS config"

@michael-ciniawsky

Sure i just wanted to ask first if there are general concerns of yours about considering this as core functionality(e.g should not be in core). I played around with postcss-load-plugins, but is unfinished at the moment, but before pollishing that module i could also work directly on it and send PR, updating while there is discussion about it. Would it be ok to first draft something out and implement and then present/ (start discussion about) it to the community for giving feedback?

@ai
Member
ai commented May 25, 2016

I totally agree, that we need common config in core.

We also started same issue, but nobody finished it yet. So half of work already done. I will send a link tomorrow (already in bed, sorry).

@michael-ciniawsky

"and require() in JS config"

Can you give a short example? :)

@michael-ciniawsky

I read partials of that issue but there was somehting with an event API mentioned and to be honest i don't know how something like that could be working :). I look that one up to get an insight.

@ai
Member
ai commented May 26, 2016

Nope, event based API is not relevant. Here is the old discuss: #477

Read it and tell what do you think about it?

@michael-ciniawsky

:+1 ok looked over it, the idea and my attempts of implementation are similiar, i currently draft out a proposal which will give an in-depth overview all possibilities for the moment and upload it, then start implementing while discussion gets on track. i call out again later today, when published.

@ai
Member
ai commented May 26, 2016

Config could be placed in:

  • postcss subsection in package.json
  • postcss.json
  • postcss.js

Also it is a good question how to name standalone configs: postcss.json or .postcss.json or postcssrc.json.

I think we should not name it .postcss.json, because this config affects on real build, so it should not be hidden (rather than .travis.yml or .eslintrc).

@michael-ciniawsky
michael-ciniawsky commented May 26, 2016 edited

Ahh:) ok i made a note about this, but was not sure to go this far, nevertheless it makes sense to make the naming clear.

When you have time please take a look at this section of the proposal, maybe thats to much:
npm config

$ npm config | -c 

Usage:
npm config set <key> <value>
npm config get [<key>]
npm config delete <key>
npm config list
npm config edit
npm set <key> <value>
npm get [<key>]
$ npm config set app:postcss:[options|plugins]:key value

Example

$ npm config set app:postcss:options:parser postcss-less
$ npm config list 

; userconfig /Users/User/.npmrc
app:postcss:options:parser = "postcss-less"
"scripts": {
 "build:css": "postcss-cli $npm_package_config_postcss"
}
npm run build:css  #use postcss-less as parser

Defaults

"name": "app"
"config": {
  "postcss": {
    "options": {
      "parser": "postcss-scss",
      "stringifier": "whatever",
      "from": "input.css",
      "to": "out.css",
      "map": { "inline": false },
    },
    "plugins": {
      "plugin1": {},
      "plugin2": {},
      "pluginHyphened3": {}
    }
  }
}

At the moment it's about the location in pkg.json, that's the second option maybe usful for cli and middlewares when task based overriding would be beneficial. Maybe better to leave that out...

@ai
Member
ai commented May 26, 2016

@michael-ciniawsky looks nice, but I think we should miss config. key and use postcss key on the root.

Also, maybe we should move our discuss to #477?

@michael-ciniawsky

yes we better leave that it's confusing and not that useful...:) kk i finish the proposal and then head over to #477 contents will be:

  • Setup (pkg.json, postcss.js | postcss.json)
  • Implementation Draft 1
  • Usage
@michael-ciniawsky

i have 2 questions:

  • Where are the args getting pass to ....process(arg) in postcss? lib/postcss.es6 ? lib/process.es6? :)
    Related to pkg.postcss.options (parser, from, to, map)
  • The location of tests related to plugins (postcss) and options (process)
    && and if it's ok to write mine with ava?
@ai
Member
ai commented May 26, 2016

@michael-ciniawsky current test are already written in ava.

postcss(plugins) is in lib/postcss.es6, process() is in lib/processor.es6

@ai ai referenced this issue in postcss/postcss-loader Jun 29, 2016
Closed

Plugins config as object instead of array #78

@texastoland
texastoland commented Jun 29, 2016 edited

Copied from my loader issue:

// postcss.config.json
module.exports = ({context, mainDir}) => ({
  import: { // must run first
    addDependencyTo: context,
    path: mainDir,
  },
  cssnext: true,
  reporter: { // must run last
    clearMessages: true,
    noIcon: true,
  },
}),

…and later with a library function:

const config = Object.entries(require(configPath))
  .filter(([key, options]) => options !== false)
  .map(([key, options]) => [require("postcss-" + kebabCase(key)), options])
  .map(([plugin, options]) => options === true ? plugin : plugin(options))
  1. I think repurposing pacakage.json would be bad. Does another popular tool follow that convention? postcss.config.js (or similar) is my preference with the option to pass any JS or JSON.
  2. The config should accept a function that takes an options object. postcss-import accepts a dynamic argument in addDependencyTo. I also pass other configurable options.
  3. I pass a boolean to plugins that don't take options or to disable them.

I could implement it in a weekend or help when it's decided.

@michael-ciniawsky

@ai, @texastoland hi, sry for the delay, problems of my ISP, connection was down until now...

If you want to help me out , please check out these repos:

Cosmiconfig

Used for loading the various possible config styles

PostCSS Load Config

The Code for each loading propose is splitted in the following submodules

const config = Object.entries(require(configPath))
  .filter(([key, options]) => options !== false)
  .map(([key, options]) => [require("postcss-" + kebabCase(key)), options])
  .map(([plugin, options]) => options === true ? plugin : plugin(options))

Your lib code goes in the direction, i'm currently playing around with 'presets', you bundle your plugins in a defined way, where each plugin has its own plugin.namespace e.g 'postcss-bem' => 'bem' inspired/derived by how PreCSS does it behind the scenes.

const plugins = [
 {
   plugin: require('postcss-plugin1'),
   namespace: 'plugin1', (can be anything, -postcss-prefix recommended)
   defaults: {}
 },
 {
   plugin: require('postcss-plugin2'),
   namespace: 'plugin2',
   defaults: {}
 },
 {
   plugin: require('postcss-plugin3-hyphened'),
   namespace: 'plugin3Hyphened', (if hyphened name, kebabed recommended)
   defaults: {}
 },
  ....,
  ....,
]

export default plugins
import plugins from './plugins'

export default function (options) {
  options = options || {}

  let preset = []

  plugins.forEach((plugin) => {
    let namespaceOptions = plugin.namespace in options
    ? options[plugin.namespace]
    : options

    let pluginOptions = {}

    Object.keys(plugin.defaults).forEach((key) => {
      pluginOptions[key] = plugin.defaults[key]
    })

    Object.keys(namespaceOptions).forEach((key) => {
      pluginOptions[key] = namespaceOptions[key]
    })

    if (namespaceOptions && pluginOptions !== 'disable') {
      preset.push(plugin.plugin(pluginOptions))
    }
  })

  return preset
}

-Example Preset

At the moment i forked a few postcss tools, primary to test the config implementation and general issues with it. If you have some spare time please feel free to help out there as well, i will update the forks with my local changes until tomorrow. There is nothing going on there at the moment, because of time leaks.

CLI
Gulp

import { task, src, dest } from 'gulp'
import postcss from 'gulp-postcss'

const plugins = []
const options = {}

task('css', () => {
   src('src/styles/*.css')
      .pipe(postcss(plugins, options))
      .pipe(dest('dest/styles/')  
})
import { task, src, dest } from 'gulp'
import postcss from 'gulp-postcss'

task('css', () => {
   src('src/styles/*.css')
      .pipe(postcss()) // no args => autoload config
      .pipe(dest('dest/styles/')  
})

Webpack

module: {
  loaders: [
    { 
         test: /^.css$/,
         loader: style!css!postcss
    }
  ]
},
postcss: () => {}
module: {
  loaders: [
    { 
         test: /^.css$/,
         loader: style!css!postcss
         // If needed, useful ¯\_(ツ)_/¯ ? 
         // loader: style!css!postcss?config='path/to/alternative/postcss.config.js'
         // loader: style!css!postcss?options={}
         // loader: style!css!postcss?plugins={}
         // loader: style!css!postcss?presets={}
    }
  ]
},
@ai
Member
ai commented Jun 30, 2016

Preset idea is great. But it could be step 1.

@michael-ciniawsky

you mean make config work and the PR's to the tools and 'introduce' presets after that?

@ai
Member
ai commented Jun 30, 2016

Everything looks awesome. Should we include it to PostCSS 5.1 or wait 5.2 after a month?

Maybe it is a good idea to implement it in postcss-gulp, postcss-loader and postcss-cli and after some time in PostCSS.

@ai
Member
ai commented Jun 30, 2016

@michael-ciniawsky yeap, it is addition feature anyway.

So, I think next step after finishing this libraries is to creating issue in gulp, CLI and webpack plugins to implement they.

Sorry, but I will be able to fix loader only from July 8 until 14 (I will start traveling to Baikanur tomorrow).

Also, @MoOx is final config looks fine to you? You have some experience in it.

@michael-ciniawsky
michael-ciniawsky commented Jun 30, 2016 edited

yep agreed, that's up to your assessment, what would be the better transition path, if postcss directly handles the config the implementation must be done only once and all tools need slight changes and tests or nothing changed at best. I take care of loader, gulp, cli (but discussion over there is needed i think, bc there is a solution for loading plugins and friends at the moment and how the transition will look like, i start one when the left edges of the config finally round up). Grunt, Broccoli, Middleware, Meteor afterwards (Grunt is more important, but i'm no grunt user ;))

@michael-ciniawsky

Sorry, but I will be able to fix loader only from July 8 until 14 (I will start traveling to Baikanur tomorrow).

np, i focus on the other things first, when someone wants to test it meanwhile, clone the fork for the time being :). i'm also busy right know, but sadly with ugly chore work for the holy capitalistic church, have fun over there and make it a nice one 👍

@texastoland
texastoland commented Jul 1, 2016 edited

Cosmiconfig

Woah cool!

PostCSS Load Config

Sincerely excellent work.

PostCSS Load Options

Duplicates postcss-load-config/lib/loadOptions.js?

PostCSS Load Plugins

Duplicates postcss-load-config/lib/loadPlugins.js? Could I open an issue there for kebab case by default?

can you please free the name postcss-load-plugins for that module on npm,

Tweeted him.

PostCSS Load Presets

Are these like build-your-own-cssnext?

@michael-ciniawsky I think I'm a good coder but this is more superbly put together than I conceived 💖

@michael-ciniawsky

Tweeted him.

👍 First, thank you very much for that

Duplicates postcss-load-config/lib/loadOptions.js?
Duplicates postcss-load-config/lib/loadPlugins.js?

Yep, i decided to publish them as separate modules for maintance reasons and for some specific usecases folks may encounter, where requiring only parts of load config is beneficial, i will rm them and update the repo soon, but e.g first need the name for postcss-load-plugins on npm.

Could I open an issue there for kebab case by default?

On a first draft i implemented kebabCase (example below), but it gets a mess quickly, when you e.g load one postcss-plugin with prefix and one without like cssnext, cssnano etc. The presets should adress that, there the namespace is set by you in the preset itself and no collision with other external modules is possible, but regarding presets, there is nothing set in stone and i'm highly open to feedback and improvements to anything related to postcss-load-presets and presets in general. Please keep in mind that i agreed with with Andrey on this common config style (without presets):

postcss.config.js

const config = {
 options: {
    parser: 'sugarss',
    from: 'src/app.sss',
    map: false,
    to: 'dist/app.css'
  },
  plugins: {
    'postcss-plugin1': null // => no options or rather the plugin defaults e.g for postcss-nested
    'postcss-plugin2' : {option1: '', option2: ''}
    'cssnext': {}
}

index.js

import postcssrc from 'postcss-load-config'
import postcss from 'postcss'

const css = fs.readFileSync('./src/app.css', 'utf8')

postcssrc.then({plugins, options} => {
  postcss(plugins).process(css, options).then((result) => console.log(result.css))
})

// postcss([ plugin1(), plugin2(options), cssnext(options) ]
//  .process(css, {parser: require('sugarss'), from: 'src/app.sss', map: false, to: 'dist/app.css'}) 

That' s set in stone, but ...

Above when i opened that issue the first example(s), they use no postcss-prefix && kebabCase style for plugin declaration and presets should use that for there plugin declarations, simply because i like it terse and clean :) first a example config with presets

postcss.config.js

const config = {
 options: {
    parser: 'sugarss',
    from: 'src/app.sss',
    map: inline,
    to: 'dist/app.css'
  },
  presets: {
    'postcss-your-company': {
       name1: {option1: '' option2: ''},  // will override the plugins defaults where set
       name2: {option1: '' option2: ''},
       nameKebabed3: {option1: '' option2: ''}
       name4: 'disable' // don't load this plugin, when not required in the current project
     },
  }

whats currently missing in postcss-load-presets and later in postcss-load-config are e.g:

  • extend preset with additional plugins anywhere in the presets plugin array
plugins: {
  'postcss-plugin1': null,
  'postcss-plugin2': null,
},
presets: {
  'postcss-your-company': {
     name1: 'disable', // will skip if not needed in current project
     name2: {option1: '' option2: ''}  // will override the plugins defaults where set
     nameKebabed3: {option1: '' option2: ''}
     ....,
     ....,
    extend: [1, 2] // postition to inject the additional plugins in this preset
    // extend: 'prepend'
    // extend:  'append'
},

// => [ name1(), plugin1(), name2(), plugin2(), nameKebabed() ]

Are these like build-your-own-cssnext?

Yes, you can name them as you like postcss-preset-name is just for extrapolation and packages like cssnext, cssnano etc. could/should use a standard way, if decided upon. Then you can bundle a nice setup with the CLI for example and so on .. :). Share either the postcss.config.js file and/or/with a preset on npm || within your team || people interested in your setup,

no

$ npm i -D 1,2,3,4...., 5....., 6.............

anymore

I think I'm a good coder but this is more superbly put together than I conceived 💖

👍 your approach is very similiar to one of mine from the past, i can speak only for myself but you where on a good track imho. Share you thoughts, help out when in the mood and file issues like crazy if nessecary :)

@texastoland
texastoland commented Jul 1, 2016 edited

'postcss-plugin1': null // => no options or rather the plugin defaults e.g for postcss-nested

Could it be true instead and false to disable? null/undefined/'disabled' all feel wrong.

it gets a mess quickly, when you e.g load one postcss-plugin with prefix and one without like cssnext, cssnano etc.

Did you already consider:

function load (plugin, options) {
  const prefix = 'postcss-'
  try {
    plugin = require(plugin) // catch here
  } catch (e) {
    if (plugin.startsWith(prefix) throw e
    else return load(prefix + kebabCase(plugin))
  }
  return options === true ? plugin() : plugin(options) // not here
}

The presets should adress that, there the namespace is set by you in the preset itself and no collision with other external modules is possible,

cssnext implemented mappings for theirs too. The only thing I dislike is needing a preset just for a convention.

postcssrc.then({plugins, options} =>

You mean postcssrc().then? Is the argument so you can override a config? That doesn't sound as useful as:

  // postcss.config.json
  module.exports = ({context, mainDir}) => ({
    import: { // must run first
      addDependencyTo: context,
      path: mainDir,
    },

There's one major use case missing. Configs should also accept functions. postcss-import accepts a reference to the postcss-loader context. In my example I'm also passing in the source path because it gets reused in other places like the context query parameter for css-loader.

cssnext, cssnano etc. could/should use a standard way, if decided upon.

CC @MoOx because cssnext is doing dynamic loading based on caniuse.

@michael-ciniawsky
michael-ciniawsky commented Jul 1, 2016 edited

Could it be true instead and false to disable? null/undefined/'disabled' all feel wrong.

Sure better alternatives welcome true/false makes more sense. Suggestion for a replacement of 'disable'

Did you already consider:

function load (plugin, options) {
const prefix= 'postcss-'
  try {
    plugin = require(plugin) // catch here
  } catch (err) {
    if (isPrefixed(plugin)) throw err

    if (isKebabed(plugin))  {
       return load(prefix + reHyphenate(plugin))
    }

    return load(prefix + plugin) 
  }

  return options === true ? plugin() : plugin(options) // not here
}

cssnext implemented mappings for theirs too. The only thing I dislike is needing a preset just for a convention.

i take a look at them when i find the time. neither do i really it's just my current approach ;)

You mean postcssrc().then? Is the argument so you can override a config?

options = {
  plugins: {
    'postcss-import': {
      path: url.parse(context.request.split('!').slice(-1)[0]).pathname // gets the path directly from webpack
      addDependencyTo: context
    }
  }
} 

// should add option if not in the common config or override it
postcssrc(options).then()...

There's one major use case missing. Configs should accept functions.

yep agree, that's a todo

@texastoland
texastoland commented Jul 1, 2016 edited

Suggestion for a replacement of 'disable'

false right?

yep agree, that's a todo

I could implement the above by Tuesday for further discussion 😊

@michael-ciniawsky

false right?

yeah right :D, thats working for both without any problems

plugins:  {
  'postcss-plugin': false // no options
  'postcss-preset': {
    plugin: false // disable
  }
} 

maybe checking for presets in the plugins section is a better solution, and skip the presets: {} key entirely.

I could implement the above by Tuesday for further discussion 😊

Yeah welcome, please help out would be really appreciated, i have some nasty chore at the moment
If you fork and PR or file issues with examples of how to basically 👍 , as you like and find the time
If it's on postcss-load-config, for now please keep the current config style for the moment, we can test and try different approaches in postcss-load-presets and then reconsider with ai.

@texastoland
texastoland commented Jul 1, 2016 edited

maybe checking for presets in the plugins section is a better solution, and skip the presets: {} key entirely.

Agreed 👍

If you fork

Already forked and cloned.

please keep the current config style for the moment,

I planned to PR true/false, the kebab shortcut, and passing functions from configs in load-config. I can change it afterward but those would make it usable in my current project!

@michael-ciniawsky

👍 i try to free some time in the evenings or rather force myself to :)

@TrySound
Member
TrySound commented Jul 1, 2016

@michael-ciniawsky Done. But publish next major version please.

@michael-ciniawsky
michael-ciniawsky commented Jul 1, 2016 edited

@TrySound thank you 👍

But publish next major version please

Sure

Can someone grap the name at the moment ?

@michael-ciniawsky

@ai what's the min node version that needs to be supported by gulp and webpack ? node 4+

@TrySound
Member
TrySound commented Jul 7, 2016

Node0.12 until the end of supported days.

@michael-ciniawsky

👎 :) :) ok, then load-config needs to be tested for node 0.12+ first. legacy support is a bitch 👯

@TrySound
Member
TrySound commented Jul 7, 2016

It's when somebody still supports 0.10 and ask you to support.

@texastoland

Haha with a PR 👍

@michael-ciniawsky
michael-ciniawsky commented Jul 7, 2016 edited

grandma's dinner 👯 hmm... sweatheart would you be so kind lifting that heavy box over there up to the attic. Of course always fun and pleasure 👍 ...

@michael-ciniawsky

needs to be done from time to time, no ape escape possible

@ai ai added the enhancement label Jul 10, 2016
@ai ai added this to the 5.2 milestone Jul 10, 2016
@RyanZim
Contributor
RyanZim commented Sep 9, 2016

@michael-ciniawsky how's that going?

@ai
Member
ai commented Sep 9, 2016

@RyanZim we need to add this libraries to gulp-postcss, postcss-loader and postcss-cli. If everything will be OK, we could more it to core. Who want to do it?

@michael-ciniawsky
michael-ciniawsky commented Sep 10, 2016 edited

@ai @RyanZim Error Handling is missing and a last review of the details for how the final accepted common config will look like. I'm sry for the insane delay on this i was interrupted by daily work a few weeks ago and at the moment i'm busy, because recently the laws in german (higher strictness) changed and most of the refugees i take care of got into serious trouble 'overnight'. Their laywers were calling in between hours, telling me chain deportations are going to be executed for most of them soon and therefore I needed to take action fast (court, move them etc...). Anyways :) in terms of the matter here, the current progress/plan is as follows.

postcss.config.js
(Done) -> {Object}

module.exports = {
  parser: 'sugarss',
  from: 'client/index.css',
  map: false,
  to: 'public/index.css',
  plugins: {
     postcss-import: {...options},
     postcss-nested: null
  }
}

(Idea) -> {Function}

env's propose is being a 'standarized' store for project specifics, CLI Flags(--map, --from, --to, --parser, --plugin), middlewares etc. store/cache local postcss config info in env. It's kind of redundant somehow, better solutions or ideas are welcome :).

module.exports = (env) => {
  parser: 'sugarss',
  from: `${env.from}`,
  map: env.map, 
  to: `${env.to}`,
  plugins: {
     postcss-import: env.import,
     postcss-nested: null
  }
}

postcss-load-config

  • Node API (Basically done)
import postcssrc from 'postcss-load-config'

const env = {} // Idea is an object to override dynamic parameters depending on the usecase/setup

postcssrc(env).then(({plugins, options}) => {
  postcss(plugins).process('css', options).then((result) => console.log(result.css))
}) 
  • Gulp

(1) Autoload (Done)

import { task, src, dest } from 'gulp'
import postcss from 'gulp-postcss'

task('css', (/* env? :) */) => { // No idea if this is possible, would be awesome
 // const env = {} e.g it's a 'special' task with requirements uncommon to the rest of the project
  src('client/css/*.{sss|css}')
    .pipe(postcss())
    //.pipe(postcss(env))
    .pipe(dest('public'))
})

(2) Autoload and Assign (Idea)

import { task, src, dest } from 'gulp'
import postcss from 'gulp-postcss'

import plugins from 'postcss-load-plugins' // -> {Array} plugins
import options from 'postcss-load-options' // -> {Object} options

// equal to  
// import { plugins, options } from 'postcss-load-config'

task('css', () => {
 // const inline = require('postcss-plugin')
 // const $ = plugins(/* env */, true) // assign === true 
                                       // -> reduce plugins array to object and assign them.
  src('client/css/*.{sss|css}')
    .pipe(postcss(plugins(/* env */), options( /* env */)))
    .pipe(postcss([$.plugin1, inline, $.plugin2, ...], options( /* env */)))
    .pipe(dest('public'))
})
  • Webpack

coming soon... :) I need to revisit the code first

To sum it up, missing pieces are

  • Error handling
  • Accepting a function as config
  • Refactor the Interface to make it more flexible (possiblity to assign plugins(?), return resolved)
  • Finish Intregrations (CLI(none), Grunt(none), Gulp(prototype), Webpack(prototype), Broccoli(none))

@ai If you are open to consider integration in postcss core it might ease user migration to the common config as standard, but on the other hand it will bloat the core and may affect browser usage. We need a decision from your side here, because the idea to assign plugins to make changing their position in the chain possible etc. would be obsolete then. Also getting all middleware maintainers together and discuss/plan how to ship it would be a good idea :).

@ai
Member
ai commented Sep 10, 2016 edited

@michael-ciniawsky why do we need env? runners could do something like:

postcssrc({ from: opts.from }).then(({plugins, options}) => {
  postcss(plugins).process('css', { ..opts, ...options }).then((result) => console.log(result.css))
}) 

But we need to set from to postcssrc to get right path for config finding.

@michael-ciniawsky

👍 yep, that's want i wanted to express with that awkward env thingy rubbish :D... it's just a an object with the right values (paths, specific options, plugin options etc.) passed through from the various build tools in the current project context, which will be merge/assigned then. Whats your opinion about exposing the possibility assigning plugins to reorder them if necessary?

@ai
Member
ai commented Sep 10, 2016

Whats your opinion about exposing the possibility assigning plugins to reorder them if necessary?

What is exposing? :) Could you show some examples? :D

@michael-ciniawsky

What is exposing? :)

It's meant like providing plugin assignment as a possibility, but it's likely that expressing it that way in this context is plain wrong :D, maybe the correct use would be e.g when talking about a 'public API' and you expose a method or the like for public usage. (When a native speaker reads this, please feel free to correct me, and if it's just utter crap, hopefully no employer ever reads this :D :D).

Could you show some examples? :D

Sure :D

postcss.config.js

const default = {
  parser: 'sugarss',
  plugins: {
     import: {},
     reporter: {},
     cssnano: {},
  }
}

module.exports = (options) => {
   parser: options.parser || default.parser,
   plugins: {
      'postcss-import': options.import || default.import,
      'postcss-nested': options.nested || null,
      'postcss-reporter': options.reporter || default.reporter,
      'cssnano': options.cssnano || default.cssnano
   }
}

gulpfile.js

import { task, src, dest } from 'gulp'

import postcss from 'gulp-postcss'
import { plugins } from 'postcss-load-config'

// Development Task
task('css-dev', () => {
  const $ = plugins({...options}, true)

  src('client/css/*.{sss|css}')
    .pipe(postcss([$.import, $.nested, ..., $.reporter]))
    .pipe(dest('public'))
})

// Production Task
task('css-prod', () => {
  const $ = plugins({...options}, true) 

  src('client/css/*.{sss|css}')
    .pipe(postcss([$.import, $.nested, $.cssnano]))
    .pipe(dest('public'))
})

/* Setting the env should be possible/done within the config, it's just used as an example here :) */

// Specific Task with additional plugin(s)
task('css-inline', () => {
  const $ = plugins({...options}, true) 
  const inline = require('postcss-plugin')

  src('client/css/*.{sss|css}')
    .pipe(postcss([$.import, inline,  $.nested, $.cssnano]))
    .pipe(dest('public'))
})
@ai
Member
ai commented Sep 10, 2016

Ouh, very complicated user case :D. I think we could not focused on it and promote it.

@RyanZim
Contributor
RyanZim commented Sep 10, 2016

FWIW:

I don't use gulp, just postcss-cli.

I have often wished that there was a way to alter the config based on whether I am building for production or development. (I would prefer not to run my css through a minifier on every file change.) If I am understanding env correctly, this would allow for this.

However, do we need a full object for env? A boolean dev/prod flag would be fine for my use-case. This could be exposed in an appropriate way by the runner (i.e. a --prod flag on the cli). Thoughts?

@michael-ciniawsky
michael-ciniawsky commented Sep 11, 2016 edited

@RyanZim

NODE_ENV

{
  "name": "css",
  "main": "postcss.config.js",
  "scripts": {
    "css:prod": "NODE_ENV=production postcss  -o dest/index.css src/index.css",
    "css:dev": "NODE_ENV=development postcss -o dest/index.css src/index.css",
  },

NPM_CONFIG_ENV

{
  "name": "css",
  "main": "postcss.config.js",
  "config": {
    "env": "development"     
  },
  "scripts": {
    "css:prod": "$npm_config_env=production postcss -o dest/index.css src/index.css",
    "css:dev": "postcss -o dest/index.css src/index.css",
  },
postcss --env|-e dev -o dest/index.css src/index.css 
postcss --env|-e prod -o dest/index.css src/index.css 

postcss.config.js

module.exports = (options) => {
  parser: 'sugarss'
  plugins: {
     'postcss-import': options.import || {...defaults}
     'cssnano': options.env === 'prod' ? options.cssnano || {...defaults} : false 
     // TODO: skip loading respective plugin when set to false 
  }
}

And/Or as you stated out, via CLI flag, but internally the flag could use NODE_ENV or npm_config_env/npm_config_postcss. The envObject i mentioned above was kind of a bad example for what i was trying to express, i intermixed the integration part into middlewares with passing values to postcss-load-config.

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