-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Request for Autoloading Plugins as a Core Functionality #813
Comments
I agree that we need common config. So we will have autoload for JSON configs abd require for JS. |
Do you want to organize all discussion for this task, find best API and implement it? I will help and guide you. |
@ai abd require JS === no js files as conf? :) |
Oops 😊 "and require() in JS config" |
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? |
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). |
Can you give a short example? :) |
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. |
Nope, event based API is not relevant. Here is the old discuss: #477 Read it and tell what do you think about it? |
:+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. |
Config could be placed in:
Also it is a good question how to name standalone configs: I think we should not name it |
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 | -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... |
@michael-ciniawsky looks nice, but I think we should miss Also, maybe we should move our discuss to #477? |
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:
|
i have 2 questions:
|
@michael-ciniawsky current test are already written in ava.
|
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))
I could implement it in a weekend or help when it's decided. |
@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: Used for loading the various possible config styles 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
} 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. 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/')
}) 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={}
}
]
}, |
Preset idea is great. But it could be step 1. |
you mean make config work and the PR's to the tools and 'introduce' presets after that? |
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. |
@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. |
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 ;)) |
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 👍 |
Woah cool! Sincerely excellent work. Duplicates Duplicates
Tweeted him. 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 💖 |
👍 First, thank you very much for that
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.
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:
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() ]
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
👍 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 :) |
Could it be
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
}
cssnext implemented mappings for theirs too. The only thing I dislike is needing a preset just for a convention.
You mean
There's one major use case missing. Configs should also accept functions.
CC @MoOx because cssnext is doing dynamic loading based on caniuse. |
Sure better alternatives welcome true/false makes more sense. Suggestion for a replacement of 'disable'
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
}
i take a look at them when i find the time. neither do i really it's just my current approach ;)
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()...
yep agree, that's a todo |
I could implement the above by Tuesday for further discussion 😊 |
@michael-ciniawsky Done. But publish next major version please. |
@TrySound thank you 👍
Sure Can someone grap the name at the moment ? |
@ai what's the min node version that needs to be supported by gulp and webpack ? node 4+ |
Node0.12 until the end of supported days. |
👎 :) :) ok, then load-config needs to be tested for node 0.12+ first. legacy support is a bitch 👯 |
It's when somebody still supports 0.10 and ask you to support. |
Haha with a PR 👍 |
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 👍 ... |
needs to be done from time to time, no ape escape possible |
@michael-ciniawsky how's that going? |
@RyanZim we need to add this libraries to |
@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 module.exports = {
parser: 'sugarss',
from: 'client/index.css',
map: false,
to: 'public/index.css',
plugins: {
postcss-import: {...options},
postcss-nested: null
}
} (Idea) -> {Function}
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
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))
})
(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'))
})
coming soon... :) I need to revisit the code first To sum it up, missing pieces are
@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 :). |
@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 |
👍 yep, that's want i wanted to express with that awkward |
What is exposing? :) Could you show some examples? :D |
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).
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'))
}) |
Ouh, very complicated user case :D. I think we could not focused on it and promote it. |
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 However, do we need a full object for |
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 |
Autoloading stopped being to be a trend in Node.js community. I am closing it since I do not plan to implement it. |
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)
plugins.js
plugins.json
package.json
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.
The text was updated successfully, but these errors were encountered: