let LazyResult = require('./lazy-result') /** * Contains plugins to process CSS. Create one `Processor` instance, * initialize its plugins, and then use that instance on numerous CSS files. * * @example * const processor = postcss([autoprefixer, precss]) * processor.process(css1).then(result => console.log(result.css)) * processor.process(css2).then(result => console.log(result.css)) */ class Processor { /** * @param {Array.|Processor} plugins PostCSS plugins. * See {@link Processor#use} for plugin format. */ constructor (plugins = []) { /** * Current PostCSS version. * * @type {string} * * @example * if (result.processor.version.split('.')[0] !== '6') { * throw new Error('This plugin works only with PostCSS 6') * } */ this.version = '7.0.32' /** * Plugins added to this processor. * * @type {pluginFunction[]} * * @example * const processor = postcss([autoprefixer, precss]) * processor.plugins.length //=> 2 */ this.plugins = this.normalize(plugins) } /** * Adds a plugin to be used as a CSS processor. * * PostCSS plugin can be in 4 formats: * * A plugin created by {@link postcss.plugin} method. * * A function. PostCSS will pass the function a @{link Root} * as the first argument and current {@link Result} instance * as the second. * * An object with a `postcss` method. PostCSS will use that method * as described in #2. * * Another {@link Processor} instance. PostCSS will copy plugins * from that instance into this one. * * Plugins can also be added by passing them as arguments when creating * a `postcss` instance (see [`postcss(plugins)`]). * * Asynchronous plugins should return a `Promise` instance. * * @param {Plugin|pluginFunction|Processor} plugin PostCSS plugin * or {@link Processor} * with plugins. * * @example * const processor = postcss() * .use(autoprefixer) * .use(precss) * * @return {Processes} Current processor to make methods chain. */ use (plugin) { this.plugins = this.plugins.concat(this.normalize([plugin])) return this } /** * Parses source CSS and returns a {@link LazyResult} Promise proxy. * Because some plugins can be asynchronous it doesn’t make * any transformations. Transformations will be applied * in the {@link LazyResult} methods. * * @param {string|toString|Result} css String with input CSS or any object * with a `toString()` method, * like a Buffer. Optionally, send * a {@link Result} instance * and the processor will take * the {@link Root} from it. * @param {processOptions} [opts] Options. * * @return {LazyResult} Promise proxy. * * @example * processor.process(css, { from: 'a.css', to: 'a.out.css' }) * .then(result => { * console.log(result.css) * }) */ process (css, opts = { }) { if (this.plugins.length === 0 && opts.parser === opts.stringifier) { if (process.env.NODE_ENV !== 'production') { if (typeof console !== 'undefined' && console.warn) { console.warn( 'You did not set any plugins, parser, or stringifier. ' + 'Right now, PostCSS does nothing. Pick plugins for your case ' + 'on https://www.postcss.parts/ and use them in postcss.config.js.' ) } } } return new LazyResult(this, css, opts) } normalize (plugins) { let normalized = [] for (let i of plugins) { if (i.postcss) i = i.postcss if (typeof i === 'object' && Array.isArray(i.plugins)) { normalized = normalized.concat(i.plugins) } else if (typeof i === 'function') { normalized.push(i) } else if (typeof i === 'object' && (i.parse || i.stringify)) { if (process.env.NODE_ENV !== 'production') { throw new Error( 'PostCSS syntaxes cannot be used as plugins. Instead, please use ' + 'one of the syntax/parser/stringifier options as outlined ' + 'in your PostCSS runner documentation.' ) } } else { throw new Error(i + ' is not a PostCSS plugin') } } return normalized } } module.exports = Processor /** * @callback builder * @param {string} part Part of generated CSS connected to this node. * @param {Node} node AST node. * @param {"start"|"end"} [type] Node’s part type. */ /** * @callback parser * * @param {string|toString} css String with input CSS or any object * with toString() method, like a Buffer. * @param {processOptions} [opts] Options with only `from` and `map` keys. * * @return {Root} PostCSS AST */ /** * @callback stringifier * * @param {Node} node Start node for stringifing. Usually {@link Root}. * @param {builder} builder Function to concatenate CSS from node’s parts * or generate string and source map. * * @return {void} */ /** * @typedef {object} syntax * @property {parser} parse Function to generate AST by string. * @property {stringifier} stringify Function to generate string by AST. */ /** * @typedef {object} toString * @property {function} toString */ /** * @callback pluginFunction * @param {Root} root Parsed input CSS. * @param {Result} result Result to set warnings or check other plugins. */ /** * @typedef {object} Plugin * @property {function} postcss PostCSS plugin function. */ /** * @typedef {object} processOptions * @property {string} from The path of the CSS source file. * You should always set `from`, * because it is used in source map * generation and syntax error messages. * @property {string} to The path where you’ll put the output * CSS file. You should always set `to` * to generate correct source maps. * @property {parser} parser Function to generate AST by string. * @property {stringifier} stringifier Class to generate string by AST. * @property {syntax} syntax Object with `parse` and `stringify`. * @property {object} map Source map options. * @property {boolean} map.inline Does source map should * be embedded in the output * CSS as a base64-encoded * comment. * @property {string|object|false|function} map.prev Source map content * from a previous * processing step * (for example, Sass). * PostCSS will try to find * previous map automatically, * so you could disable it by * `false` value. * @property {boolean} map.sourcesContent Does PostCSS should set * the origin content to map. * @property {string|false} map.annotation Does PostCSS should set * annotation comment to map. * @property {string} map.from Override `from` in map’s * sources`. */