Comparing version 8.4.17 to 8.4.31
import Container, { ContainerProps } from './container.js' | ||
interface AtRuleRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
*/ | ||
before?: string | ||
declare namespace AtRule { | ||
export interface AtRuleRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols after the last child of the node to the end of the node. | ||
*/ | ||
after?: string | ||
/** | ||
* The space symbols after the last child of the node to the end of the node. | ||
*/ | ||
after?: string | ||
/** | ||
* The space between the at-rule name and its parameters. | ||
*/ | ||
afterName?: string | ||
/** | ||
* The space between the at-rule name and its parameters. | ||
*/ | ||
afterName?: string | ||
/** | ||
* The space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
*/ | ||
before?: string | ||
/** | ||
* The symbols between the last parameter and `{` for rules. | ||
*/ | ||
between?: string | ||
/** | ||
* The symbols between the last parameter and `{` for rules. | ||
*/ | ||
between?: string | ||
/** | ||
* Contains `true` if the last child has an (optional) semicolon. | ||
*/ | ||
semicolon?: boolean | ||
/** | ||
* The rule’s selector with comments. | ||
*/ | ||
params?: { | ||
raw: string | ||
value: string | ||
} | ||
/** | ||
* The rule’s selector with comments. | ||
*/ | ||
params?: { | ||
value: string | ||
raw: string | ||
/** | ||
* Contains `true` if the last child has an (optional) semicolon. | ||
*/ | ||
semicolon?: boolean | ||
} | ||
} | ||
export interface AtRuleProps extends ContainerProps { | ||
/** Name of the at-rule. */ | ||
name: string | ||
/** Parameters following the name of the at-rule. */ | ||
params?: string | number | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: AtRuleRaws | ||
export interface AtRuleProps extends ContainerProps { | ||
/** Name of the at-rule. */ | ||
name: string | ||
/** Parameters following the name of the at-rule. */ | ||
params?: number | string | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: AtRuleRaws | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { AtRule_ as default } | ||
} | ||
@@ -59,3 +64,3 @@ | ||
* | ||
* If it’s followed in the CSS by a {} block, this node will have | ||
* If it’s followed in the CSS by a `{}` block, this node will have | ||
* a nodes property representing its children. | ||
@@ -74,7 +79,3 @@ * | ||
*/ | ||
export default class AtRule extends Container { | ||
type: 'atrule' | ||
parent: Container | undefined | ||
raws: AtRuleRaws | ||
declare class AtRule_ extends Container { | ||
/** | ||
@@ -90,6 +91,5 @@ * The at-rule’s name immediately follows the `@`. | ||
name: string | ||
/** | ||
* The at-rule’s parameters, the values that follow the at-rule’s name | ||
* but precede any {} block. | ||
* but precede any `{}` block. | ||
* | ||
@@ -103,8 +103,17 @@ * ```js | ||
params: string | ||
parent: Container | undefined | ||
constructor(defaults?: AtRuleProps) | ||
assign(overrides: object | AtRuleProps): this | ||
clone(overrides?: Partial<AtRuleProps>): this | ||
cloneBefore(overrides?: Partial<AtRuleProps>): this | ||
cloneAfter(overrides?: Partial<AtRuleProps>): this | ||
raws: AtRule.AtRuleRaws | ||
type: 'atrule' | ||
constructor(defaults?: AtRule.AtRuleProps) | ||
assign(overrides: AtRule.AtRuleProps | object): this | ||
clone(overrides?: Partial<AtRule.AtRuleProps>): AtRule | ||
cloneAfter(overrides?: Partial<AtRule.AtRuleProps>): AtRule | ||
cloneBefore(overrides?: Partial<AtRule.AtRuleProps>): AtRule | ||
} | ||
declare class AtRule extends AtRule_ {} | ||
export = AtRule |
import Container from './container.js' | ||
import Node, { NodeProps } from './node.js' | ||
interface CommentRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols before the node. | ||
*/ | ||
before?: string | ||
declare namespace Comment { | ||
export interface CommentRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols before the node. | ||
*/ | ||
before?: string | ||
/** | ||
* The space symbols between `/*` and the comment’s text. | ||
*/ | ||
left?: string | ||
/** | ||
* The space symbols between `/*` and the comment’s text. | ||
*/ | ||
left?: string | ||
/** | ||
* The space symbols between the comment’s text. | ||
*/ | ||
right?: string | ||
} | ||
/** | ||
* The space symbols between the comment’s text. | ||
*/ | ||
right?: string | ||
} | ||
export interface CommentProps extends NodeProps { | ||
/** Content of the comment. */ | ||
text: string | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: CommentRaws | ||
export interface CommentProps extends NodeProps { | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: CommentRaws | ||
/** Content of the comment. */ | ||
text: string | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Comment_ as default } | ||
} | ||
/** | ||
* Represents a comment between declarations or statements (rule and at-rules). | ||
* It represents a class that handles | ||
* [CSS comments](https://developer.mozilla.org/en-US/docs/Web/CSS/Comments) | ||
* | ||
* ```js | ||
* Once (root, { Comment }) { | ||
* let note = new Comment({ text: 'Note: …' }) | ||
* const note = new Comment({ text: 'Note: …' }) | ||
* root.append(note) | ||
@@ -38,10 +44,9 @@ * } | ||
* | ||
* Comments inside selectors, at-rule parameters, or declaration values | ||
* will be stored in the `raws` properties explained above. | ||
* Remember that CSS comments inside selectors, at-rule parameters, | ||
* or declaration values will be stored in the `raws` properties | ||
* explained above. | ||
*/ | ||
export default class Comment extends Node { | ||
type: 'comment' | ||
declare class Comment_ extends Node { | ||
parent: Container | undefined | ||
raws: CommentRaws | ||
raws: Comment.CommentRaws | ||
/** | ||
@@ -52,7 +57,13 @@ * The comment's text. | ||
constructor(defaults?: CommentProps) | ||
assign(overrides: object | CommentProps): this | ||
clone(overrides?: Partial<CommentProps>): this | ||
cloneBefore(overrides?: Partial<CommentProps>): this | ||
cloneAfter(overrides?: Partial<CommentProps>): this | ||
type: 'comment' | ||
constructor(defaults?: Comment.CommentProps) | ||
assign(overrides: Comment.CommentProps | object): this | ||
clone(overrides?: Partial<Comment.CommentProps>): Comment | ||
cloneAfter(overrides?: Partial<Comment.CommentProps>): Comment | ||
cloneBefore(overrides?: Partial<Comment.CommentProps>): Comment | ||
} | ||
declare class Comment extends Comment_ {} | ||
export = Comment |
@@ -1,21 +0,26 @@ | ||
import Node, { ChildNode, NodeProps, ChildProps } from './node.js' | ||
import AtRule from './at-rule.js' | ||
import Comment from './comment.js' | ||
import Declaration from './declaration.js' | ||
import Comment from './comment.js' | ||
import AtRule from './at-rule.js' | ||
import Node, { ChildNode, ChildProps, NodeProps } from './node.js' | ||
import Rule from './rule.js' | ||
interface ValueOptions { | ||
/** | ||
* An array of property names. | ||
*/ | ||
props?: string[] | ||
declare namespace Container { | ||
export interface ValueOptions { | ||
/** | ||
* String that’s used to narrow down values and speed up the regexp search. | ||
*/ | ||
fast?: string | ||
/** | ||
* String that’s used to narrow down values and speed up the regexp search. | ||
*/ | ||
fast?: string | ||
} | ||
/** | ||
* An array of property names. | ||
*/ | ||
props?: string[] | ||
} | ||
export interface ContainerProps extends NodeProps { | ||
nodes?: (ChildNode | ChildProps)[] | ||
export interface ContainerProps extends NodeProps { | ||
nodes?: (ChildNode | ChildProps)[] | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Container_ as default } | ||
} | ||
@@ -30,5 +35,3 @@ | ||
*/ | ||
export default abstract class Container< | ||
Child extends Node = ChildNode | ||
> extends Node { | ||
declare abstract class Container_<Child extends Node = ChildNode> extends Node { | ||
/** | ||
@@ -47,19 +50,30 @@ * An array containing the container’s children. | ||
/** | ||
* The container’s first child. | ||
* Inserts new nodes to the end of the container. | ||
* | ||
* ```js | ||
* rule.first === rules.nodes[0] | ||
* const decl1 = new Declaration({ prop: 'color', value: 'black' }) | ||
* const decl2 = new Declaration({ prop: 'background-color', value: 'white' }) | ||
* rule.append(decl1, decl2) | ||
* | ||
* root.append({ name: 'charset', params: '"UTF-8"' }) // at-rule | ||
* root.append({ selector: 'a' }) // rule | ||
* rule.append({ prop: 'color', value: 'black' }) // declaration | ||
* rule.append({ text: 'Comment' }) // comment | ||
* | ||
* root.append('a {}') | ||
* root.first.append('color: black; z-index: 1') | ||
* ``` | ||
*/ | ||
get first(): Child | undefined | ||
/** | ||
* The container’s last child. | ||
* | ||
* ```js | ||
* rule.last === rule.nodes[rule.nodes.length - 1] | ||
* ``` | ||
* @param nodes New nodes. | ||
* @return This node for methods chain. | ||
*/ | ||
get last(): Child | undefined | ||
append( | ||
...nodes: (ChildProps | ChildProps[] | Node | Node[] | string | string[])[] | ||
): this | ||
assign(overrides: Container.ContainerProps | object): this | ||
clone(overrides?: Partial<Container.ContainerProps>): Container<Child> | ||
cloneAfter(overrides?: Partial<Container.ContainerProps>): Container<Child> | ||
cloneBefore(overrides?: Partial<Container.ContainerProps>): Container<Child> | ||
/** | ||
@@ -103,127 +117,53 @@ * Iterates through the container’s immediate children, | ||
/** | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each node. | ||
* Returns `true` if callback returns `true` | ||
* for all of the container’s children. | ||
* | ||
* Like container.each(), this method is safe to use | ||
* if you are mutating arrays during iteration. | ||
* | ||
* If you only need to iterate through the container’s immediate children, | ||
* use `Container#each`. | ||
* | ||
* ```js | ||
* root.walk(node => { | ||
* // Traverses all descendant nodes. | ||
* }) | ||
* const noPrefixes = rule.every(i => i.prop[0] !== '-') | ||
* ``` | ||
* | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
* @param condition Iterator returns true or false. | ||
* @return Is every child pass condition. | ||
*/ | ||
walk( | ||
callback: (node: ChildNode, index: number) => false | void | ||
): false | undefined | ||
every( | ||
condition: (node: Child, index: number, nodes: Child[]) => boolean | ||
): boolean | ||
/** | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each declaration node. | ||
* Returns a `child`’s index within the `Container#nodes` array. | ||
* | ||
* If you pass a filter, iteration will only happen over declarations | ||
* with matching properties. | ||
* | ||
* ```js | ||
* root.walkDecls(decl => { | ||
* checkPropertySupport(decl.prop) | ||
* }) | ||
* | ||
* root.walkDecls('border-radius', decl => { | ||
* decl.remove() | ||
* }) | ||
* | ||
* root.walkDecls(/^background/, decl => { | ||
* decl.value = takeFirstColorFromGradient(decl.value) | ||
* }) | ||
* rule.index( rule.nodes[2] ) //=> 2 | ||
* ``` | ||
* | ||
* Like `Container#each`, this method is safe | ||
* to use if you are mutating arrays during iteration. | ||
* | ||
* @param prop String or regular expression to filter declarations | ||
* by property name. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
* @param child Child of the current container. | ||
* @return Child index. | ||
*/ | ||
walkDecls( | ||
propFilter: string | RegExp, | ||
callback: (decl: Declaration, index: number) => false | void | ||
): false | undefined | ||
walkDecls( | ||
callback: (decl: Declaration, index: number) => false | void | ||
): false | undefined | ||
index(child: Child | number): number | ||
/** | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each rule node. | ||
* Insert new node after old node within the container. | ||
* | ||
* If you pass a filter, iteration will only happen over rules | ||
* with matching selectors. | ||
* | ||
* Like `Container#each`, this method is safe | ||
* to use if you are mutating arrays during iteration. | ||
* | ||
* ```js | ||
* const selectors = [] | ||
* root.walkRules(rule => { | ||
* selectors.push(rule.selector) | ||
* }) | ||
* console.log(`Your CSS uses ${ selectors.length } selectors`) | ||
* ``` | ||
* | ||
* @param selector String or regular expression to filter rules by selector. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
* @param oldNode Child or child’s index. | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
*/ | ||
walkRules( | ||
selectorFilter: string | RegExp, | ||
callback: (rule: Rule, index: number) => false | void | ||
): false | undefined | ||
walkRules( | ||
callback: (rule: Rule, index: number) => false | void | ||
): false | undefined | ||
insertAfter( | ||
oldNode: Child | number, | ||
newNode: Child | Child[] | ChildProps | ChildProps[] | string | string[] | ||
): this | ||
/** | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each at-rule node. | ||
* Insert new node before old node within the container. | ||
* | ||
* If you pass a filter, iteration will only happen over at-rules | ||
* that have matching names. | ||
* | ||
* Like `Container#each`, this method is safe | ||
* to use if you are mutating arrays during iteration. | ||
* | ||
* ```js | ||
* root.walkAtRules(rule => { | ||
* if (isOld(rule.name)) rule.remove() | ||
* }) | ||
* | ||
* let first = false | ||
* root.walkAtRules('charset', rule => { | ||
* if (!first) { | ||
* first = true | ||
* } else { | ||
* rule.remove() | ||
* } | ||
* }) | ||
* rule.insertBefore(decl, decl.clone({ prop: '-webkit-' + decl.prop })) | ||
* ``` | ||
* | ||
* @param name String or regular expression to filter at-rules by name. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
* @param oldNode Child or child’s index. | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
*/ | ||
walkAtRules( | ||
nameFilter: string | RegExp, | ||
callback: (atRule: AtRule, index: number) => false | void | ||
): false | undefined | ||
walkAtRules( | ||
callback: (atRule: AtRule, index: number) => false | void | ||
): false | undefined | ||
insertBefore( | ||
oldNode: Child | number, | ||
newNode: Child | Child[] | ChildProps | ChildProps[] | string | string[] | ||
): this | ||
@@ -247,34 +187,3 @@ /** | ||
walkComments( | ||
callback: (comment: Comment, indexed: number) => false | void | ||
): false | undefined | ||
walkComments( | ||
callback: (comment: Comment, indexed: number) => false | void | ||
): false | undefined | ||
/** | ||
* Inserts new nodes to the end of the container. | ||
* | ||
* ```js | ||
* const decl1 = new Declaration({ prop: 'color', value: 'black' }) | ||
* const decl2 = new Declaration({ prop: 'background-color', value: 'white' }) | ||
* rule.append(decl1, decl2) | ||
* | ||
* root.append({ name: 'charset', params: '"UTF-8"' }) // at-rule | ||
* root.append({ selector: 'a' }) // rule | ||
* rule.append({ prop: 'color', value: 'black' }) // declaration | ||
* rule.append({ text: 'Comment' }) // comment | ||
* | ||
* root.append('a {}') | ||
* root.first.append('color: black; z-index: 1') | ||
* ``` | ||
* | ||
* @param nodes New nodes. | ||
* @return This node for methods chain. | ||
*/ | ||
append( | ||
...nodes: (Node | Node[] | ChildProps | ChildProps[] | string | string[])[] | ||
): this | ||
/** | ||
* Inserts new nodes to the start of the container. | ||
@@ -300,5 +209,4 @@ * | ||
prepend( | ||
...nodes: (Node | Node[] | ChildProps | ChildProps[] | string | string[])[] | ||
...nodes: (ChildProps | ChildProps[] | Node | Node[] | string | string[])[] | ||
): this | ||
/** | ||
@@ -317,30 +225,15 @@ * Add child to the end of the node. | ||
/** | ||
* Insert new node before old node within the container. | ||
* Removes all children from the container | ||
* and cleans their parent properties. | ||
* | ||
* ```js | ||
* rule.insertBefore(decl, decl.clone({ prop: '-webkit-' + decl.prop })) | ||
* rule.removeAll() | ||
* rule.nodes.length //=> 0 | ||
* ``` | ||
* | ||
* @param oldNode Child or child’s index. | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
*/ | ||
insertBefore( | ||
oldNode: Child | number, | ||
newNode: Child | ChildProps | string | Child[] | ChildProps[] | string[] | ||
): this | ||
removeAll(): this | ||
/** | ||
* Insert new node after old node within the container. | ||
* | ||
* @param oldNode Child or child’s index. | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
*/ | ||
insertAfter( | ||
oldNode: Child | number, | ||
newNode: Child | ChildProps | string | Child[] | ChildProps[] | string[] | ||
): this | ||
/** | ||
* Removes node from the container and cleans the parent properties | ||
@@ -361,14 +254,6 @@ * from the node and its children. | ||
/** | ||
* Removes all children from the container | ||
* and cleans their parent properties. | ||
* | ||
* ```js | ||
* rule.removeAll() | ||
* rule.nodes.length //=> 0 | ||
* ``` | ||
* | ||
* @return This node for methods chain. | ||
*/ | ||
removeAll(): this | ||
replaceValues( | ||
pattern: RegExp | string, | ||
replaced: { (substring: string, ...args: any[]): string } | string | ||
): this | ||
@@ -399,23 +284,19 @@ /** | ||
replaceValues( | ||
pattern: string | RegExp, | ||
options: ValueOptions, | ||
replaced: string | { (substring: string, ...args: any[]): string } | ||
pattern: RegExp | string, | ||
options: Container.ValueOptions, | ||
replaced: { (substring: string, ...args: any[]): string } | string | ||
): this | ||
replaceValues( | ||
pattern: string | RegExp, | ||
replaced: string | { (substring: string, ...args: any[]): string } | ||
): this | ||
/** | ||
* Returns `true` if callback returns `true` | ||
* for all of the container’s children. | ||
* Returns `true` if callback returns `true` for (at least) one | ||
* of the container’s children. | ||
* | ||
* ```js | ||
* const noPrefixes = rule.every(i => i.prop[0] !== '-') | ||
* const hasPrefix = rule.some(i => i.prop[0] === '-') | ||
* ``` | ||
* | ||
* @param condition Iterator returns true or false. | ||
* @return Is every child pass condition. | ||
* @return Is some child pass condition. | ||
*/ | ||
every( | ||
some( | ||
condition: (node: Child, index: number, nodes: Child[]) => boolean | ||
@@ -425,27 +306,156 @@ ): boolean | ||
/** | ||
* Returns `true` if callback returns `true` for (at least) one | ||
* of the container’s children. | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each node. | ||
* | ||
* Like container.each(), this method is safe to use | ||
* if you are mutating arrays during iteration. | ||
* | ||
* If you only need to iterate through the container’s immediate children, | ||
* use `Container#each`. | ||
* | ||
* ```js | ||
* const hasPrefix = rule.some(i => i.prop[0] === '-') | ||
* root.walk(node => { | ||
* // Traverses all descendant nodes. | ||
* }) | ||
* ``` | ||
* | ||
* @param condition Iterator returns true or false. | ||
* @return Is some child pass condition. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
*/ | ||
some( | ||
condition: (node: Child, index: number, nodes: Child[]) => boolean | ||
): boolean | ||
walk( | ||
callback: (node: ChildNode, index: number) => false | void | ||
): false | undefined | ||
/** | ||
* Returns a `child`’s index within the `Container#nodes` array. | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each at-rule node. | ||
* | ||
* If you pass a filter, iteration will only happen over at-rules | ||
* that have matching names. | ||
* | ||
* Like `Container#each`, this method is safe | ||
* to use if you are mutating arrays during iteration. | ||
* | ||
* ```js | ||
* rule.index( rule.nodes[2] ) //=> 2 | ||
* root.walkAtRules(rule => { | ||
* if (isOld(rule.name)) rule.remove() | ||
* }) | ||
* | ||
* let first = false | ||
* root.walkAtRules('charset', rule => { | ||
* if (!first) { | ||
* first = true | ||
* } else { | ||
* rule.remove() | ||
* } | ||
* }) | ||
* ``` | ||
* | ||
* @param child Child of the current container. | ||
* @return Child index. | ||
* @param name String or regular expression to filter at-rules by name. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
*/ | ||
index(child: Child | number): number | ||
walkAtRules( | ||
nameFilter: RegExp | string, | ||
callback: (atRule: AtRule, index: number) => false | void | ||
): false | undefined | ||
walkAtRules( | ||
callback: (atRule: AtRule, index: number) => false | void | ||
): false | undefined | ||
walkComments( | ||
callback: (comment: Comment, indexed: number) => false | void | ||
): false | undefined | ||
walkComments( | ||
callback: (comment: Comment, indexed: number) => false | void | ||
): false | undefined | ||
/** | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each declaration node. | ||
* | ||
* If you pass a filter, iteration will only happen over declarations | ||
* with matching properties. | ||
* | ||
* ```js | ||
* root.walkDecls(decl => { | ||
* checkPropertySupport(decl.prop) | ||
* }) | ||
* | ||
* root.walkDecls('border-radius', decl => { | ||
* decl.remove() | ||
* }) | ||
* | ||
* root.walkDecls(/^background/, decl => { | ||
* decl.value = takeFirstColorFromGradient(decl.value) | ||
* }) | ||
* ``` | ||
* | ||
* Like `Container#each`, this method is safe | ||
* to use if you are mutating arrays during iteration. | ||
* | ||
* @param prop String or regular expression to filter declarations | ||
* by property name. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
*/ | ||
walkDecls( | ||
propFilter: RegExp | string, | ||
callback: (decl: Declaration, index: number) => false | void | ||
): false | undefined | ||
walkDecls( | ||
callback: (decl: Declaration, index: number) => false | void | ||
): false | undefined | ||
/** | ||
* Traverses the container’s descendant nodes, calling callback | ||
* for each rule node. | ||
* | ||
* If you pass a filter, iteration will only happen over rules | ||
* with matching selectors. | ||
* | ||
* Like `Container#each`, this method is safe | ||
* to use if you are mutating arrays during iteration. | ||
* | ||
* ```js | ||
* const selectors = [] | ||
* root.walkRules(rule => { | ||
* selectors.push(rule.selector) | ||
* }) | ||
* console.log(`Your CSS uses ${ selectors.length } selectors`) | ||
* ``` | ||
* | ||
* @param selector String or regular expression to filter rules by selector. | ||
* @param callback Iterator receives each node and index. | ||
* @return Returns `false` if iteration was broke. | ||
*/ | ||
walkRules( | ||
selectorFilter: RegExp | string, | ||
callback: (rule: Rule, index: number) => false | void | ||
): false | undefined | ||
walkRules( | ||
callback: (rule: Rule, index: number) => false | void | ||
): false | undefined | ||
/** | ||
* The container’s first child. | ||
* | ||
* ```js | ||
* rule.first === rules.nodes[0] | ||
* ``` | ||
*/ | ||
get first(): Child | undefined | ||
/** | ||
* The container’s last child. | ||
* | ||
* ```js | ||
* rule.last === rule.nodes[rule.nodes.length - 1] | ||
* ``` | ||
*/ | ||
get last(): Child | undefined | ||
} | ||
declare class Container<Child extends Node = ChildNode> extends Container_<Child> {} | ||
export = Container |
@@ -28,8 +28,20 @@ 'use strict' | ||
class Container extends Node { | ||
push(child) { | ||
child.parent = this | ||
this.proxyOf.nodes.push(child) | ||
append(...children) { | ||
for (let child of children) { | ||
let nodes = this.normalize(child, this.last) | ||
for (let node of nodes) this.proxyOf.nodes.push(node) | ||
} | ||
this.markDirty() | ||
return this | ||
} | ||
cleanRaws(keepBetween) { | ||
super.cleanRaws(keepBetween) | ||
if (this.nodes) { | ||
for (let node of this.nodes) node.cleanRaws(keepBetween) | ||
} | ||
} | ||
each(callback) { | ||
@@ -52,114 +64,84 @@ if (!this.proxyOf.nodes) return undefined | ||
walk(callback) { | ||
return this.each((child, i) => { | ||
let result | ||
try { | ||
result = callback(child, i) | ||
} catch (e) { | ||
throw child.addToError(e) | ||
} | ||
if (result !== false && child.walk) { | ||
result = child.walk(callback) | ||
} | ||
return result | ||
}) | ||
every(condition) { | ||
return this.nodes.every(condition) | ||
} | ||
walkDecls(prop, callback) { | ||
if (!callback) { | ||
callback = prop | ||
return this.walk((child, i) => { | ||
if (child.type === 'decl') { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
if (prop instanceof RegExp) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'decl' && prop.test(child.prop)) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
return this.walk((child, i) => { | ||
if (child.type === 'decl' && child.prop === prop) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
getIterator() { | ||
if (!this.lastEach) this.lastEach = 0 | ||
if (!this.indexes) this.indexes = {} | ||
walkRules(selector, callback) { | ||
if (!callback) { | ||
callback = selector | ||
this.lastEach += 1 | ||
let iterator = this.lastEach | ||
this.indexes[iterator] = 0 | ||
return this.walk((child, i) => { | ||
if (child.type === 'rule') { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
if (selector instanceof RegExp) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'rule' && selector.test(child.selector)) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
return this.walk((child, i) => { | ||
if (child.type === 'rule' && child.selector === selector) { | ||
return callback(child, i) | ||
} | ||
}) | ||
return iterator | ||
} | ||
walkAtRules(name, callback) { | ||
if (!callback) { | ||
callback = name | ||
return this.walk((child, i) => { | ||
if (child.type === 'atrule') { | ||
return callback(child, i) | ||
getProxyProcessor() { | ||
return { | ||
get(node, prop) { | ||
if (prop === 'proxyOf') { | ||
return node | ||
} else if (!node[prop]) { | ||
return node[prop] | ||
} else if ( | ||
prop === 'each' || | ||
(typeof prop === 'string' && prop.startsWith('walk')) | ||
) { | ||
return (...args) => { | ||
return node[prop]( | ||
...args.map(i => { | ||
if (typeof i === 'function') { | ||
return (child, index) => i(child.toProxy(), index) | ||
} else { | ||
return i | ||
} | ||
}) | ||
) | ||
} | ||
} else if (prop === 'every' || prop === 'some') { | ||
return cb => { | ||
return node[prop]((child, ...other) => | ||
cb(child.toProxy(), ...other) | ||
) | ||
} | ||
} else if (prop === 'root') { | ||
return () => node.root().toProxy() | ||
} else if (prop === 'nodes') { | ||
return node.nodes.map(i => i.toProxy()) | ||
} else if (prop === 'first' || prop === 'last') { | ||
return node[prop].toProxy() | ||
} else { | ||
return node[prop] | ||
} | ||
}) | ||
} | ||
if (name instanceof RegExp) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'atrule' && name.test(child.name)) { | ||
return callback(child, i) | ||
}, | ||
set(node, prop, value) { | ||
if (node[prop] === value) return true | ||
node[prop] = value | ||
if (prop === 'name' || prop === 'params' || prop === 'selector') { | ||
node.markDirty() | ||
} | ||
}) | ||
return true | ||
} | ||
} | ||
return this.walk((child, i) => { | ||
if (child.type === 'atrule' && child.name === name) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
walkComments(callback) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'comment') { | ||
return callback(child, i) | ||
} | ||
}) | ||
index(child) { | ||
if (typeof child === 'number') return child | ||
if (child.proxyOf) child = child.proxyOf | ||
return this.proxyOf.nodes.indexOf(child) | ||
} | ||
append(...children) { | ||
for (let child of children) { | ||
let nodes = this.normalize(child, this.last) | ||
for (let node of nodes) this.proxyOf.nodes.push(node) | ||
} | ||
insertAfter(exist, add) { | ||
let existIndex = this.index(exist) | ||
let nodes = this.normalize(add, this.proxyOf.nodes[existIndex]).reverse() | ||
existIndex = this.index(exist) | ||
for (let node of nodes) this.proxyOf.nodes.splice(existIndex + 1, 0, node) | ||
this.markDirty() | ||
return this | ||
} | ||
prepend(...children) { | ||
children = children.reverse() | ||
for (let child of children) { | ||
let nodes = this.normalize(child, this.first, 'prepend').reverse() | ||
for (let node of nodes) this.proxyOf.nodes.unshift(node) | ||
for (let id in this.indexes) { | ||
this.indexes[id] = this.indexes[id] + nodes.length | ||
let index | ||
for (let id in this.indexes) { | ||
index = this.indexes[id] | ||
if (existIndex < index) { | ||
this.indexes[id] = index + nodes.length | ||
} | ||
@@ -173,12 +155,5 @@ } | ||
cleanRaws(keepBetween) { | ||
super.cleanRaws(keepBetween) | ||
if (this.nodes) { | ||
for (let node of this.nodes) node.cleanRaws(keepBetween) | ||
} | ||
} | ||
insertBefore(exist, add) { | ||
let existIndex = this.index(exist) | ||
let type = exist === 0 ? 'prepend' : false | ||
let type = existIndex === 0 ? 'prepend' : false | ||
let nodes = this.normalize(add, this.proxyOf.nodes[existIndex], type).reverse() | ||
@@ -201,14 +176,60 @@ existIndex = this.index(exist) | ||
insertAfter(exist, add) { | ||
let existIndex = this.index(exist) | ||
let nodes = this.normalize(add, this.proxyOf.nodes[existIndex]).reverse() | ||
existIndex = this.index(exist) | ||
for (let node of nodes) this.proxyOf.nodes.splice(existIndex + 1, 0, node) | ||
normalize(nodes, sample) { | ||
if (typeof nodes === 'string') { | ||
nodes = cleanSource(parse(nodes).nodes) | ||
} else if (Array.isArray(nodes)) { | ||
nodes = nodes.slice(0) | ||
for (let i of nodes) { | ||
if (i.parent) i.parent.removeChild(i, 'ignore') | ||
} | ||
} else if (nodes.type === 'root' && this.type !== 'document') { | ||
nodes = nodes.nodes.slice(0) | ||
for (let i of nodes) { | ||
if (i.parent) i.parent.removeChild(i, 'ignore') | ||
} | ||
} else if (nodes.type) { | ||
nodes = [nodes] | ||
} else if (nodes.prop) { | ||
if (typeof nodes.value === 'undefined') { | ||
throw new Error('Value field is missed in node creation') | ||
} else if (typeof nodes.value !== 'string') { | ||
nodes.value = String(nodes.value) | ||
} | ||
nodes = [new Declaration(nodes)] | ||
} else if (nodes.selector) { | ||
nodes = [new Rule(nodes)] | ||
} else if (nodes.name) { | ||
nodes = [new AtRule(nodes)] | ||
} else if (nodes.text) { | ||
nodes = [new Comment(nodes)] | ||
} else { | ||
throw new Error('Unknown node type in node creation') | ||
} | ||
let index | ||
for (let id in this.indexes) { | ||
index = this.indexes[id] | ||
if (existIndex < index) { | ||
this.indexes[id] = index + nodes.length | ||
let processed = nodes.map(i => { | ||
/* c8 ignore next */ | ||
if (!i[my]) Container.rebuild(i) | ||
i = i.proxyOf | ||
if (i.parent) i.parent.removeChild(i) | ||
if (i[isClean]) markDirtyUp(i) | ||
if (typeof i.raws.before === 'undefined') { | ||
if (sample && typeof sample.raws.before !== 'undefined') { | ||
i.raws.before = sample.raws.before.replace(/\S/g, '') | ||
} | ||
} | ||
i.parent = this.proxyOf | ||
return i | ||
}) | ||
return processed | ||
} | ||
prepend(...children) { | ||
children = children.reverse() | ||
for (let child of children) { | ||
let nodes = this.normalize(child, this.first, 'prepend').reverse() | ||
for (let node of nodes) this.proxyOf.nodes.unshift(node) | ||
for (let id in this.indexes) { | ||
this.indexes[id] = this.indexes[id] + nodes.length | ||
} | ||
} | ||
@@ -221,2 +242,17 @@ | ||
push(child) { | ||
child.parent = this | ||
this.proxyOf.nodes.push(child) | ||
return this | ||
} | ||
removeAll() { | ||
for (let node of this.proxyOf.nodes) node.parent = undefined | ||
this.proxyOf.nodes = [] | ||
this.markDirty() | ||
return this | ||
} | ||
removeChild(child) { | ||
@@ -240,11 +276,2 @@ child = this.index(child) | ||
removeAll() { | ||
for (let node of this.proxyOf.nodes) node.parent = undefined | ||
this.proxyOf.nodes = [] | ||
this.markDirty() | ||
return this | ||
} | ||
replaceValues(pattern, opts, callback) { | ||
@@ -268,6 +295,2 @@ if (!callback) { | ||
every(condition) { | ||
return this.nodes.every(condition) | ||
} | ||
some(condition) { | ||
@@ -277,127 +300,104 @@ return this.nodes.some(condition) | ||
index(child) { | ||
if (typeof child === 'number') return child | ||
if (child.proxyOf) child = child.proxyOf | ||
return this.proxyOf.nodes.indexOf(child) | ||
} | ||
walk(callback) { | ||
return this.each((child, i) => { | ||
let result | ||
try { | ||
result = callback(child, i) | ||
} catch (e) { | ||
throw child.addToError(e) | ||
} | ||
if (result !== false && child.walk) { | ||
result = child.walk(callback) | ||
} | ||
get first() { | ||
if (!this.proxyOf.nodes) return undefined | ||
return this.proxyOf.nodes[0] | ||
return result | ||
}) | ||
} | ||
get last() { | ||
if (!this.proxyOf.nodes) return undefined | ||
return this.proxyOf.nodes[this.proxyOf.nodes.length - 1] | ||
walkAtRules(name, callback) { | ||
if (!callback) { | ||
callback = name | ||
return this.walk((child, i) => { | ||
if (child.type === 'atrule') { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
if (name instanceof RegExp) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'atrule' && name.test(child.name)) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
return this.walk((child, i) => { | ||
if (child.type === 'atrule' && child.name === name) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
normalize(nodes, sample) { | ||
if (typeof nodes === 'string') { | ||
nodes = cleanSource(parse(nodes).nodes) | ||
} else if (Array.isArray(nodes)) { | ||
nodes = nodes.slice(0) | ||
for (let i of nodes) { | ||
if (i.parent) i.parent.removeChild(i, 'ignore') | ||
walkComments(callback) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'comment') { | ||
return callback(child, i) | ||
} | ||
} else if (nodes.type === 'root' && this.type !== 'document') { | ||
nodes = nodes.nodes.slice(0) | ||
for (let i of nodes) { | ||
if (i.parent) i.parent.removeChild(i, 'ignore') | ||
} | ||
} else if (nodes.type) { | ||
nodes = [nodes] | ||
} else if (nodes.prop) { | ||
if (typeof nodes.value === 'undefined') { | ||
throw new Error('Value field is missed in node creation') | ||
} else if (typeof nodes.value !== 'string') { | ||
nodes.value = String(nodes.value) | ||
} | ||
nodes = [new Declaration(nodes)] | ||
} else if (nodes.selector) { | ||
nodes = [new Rule(nodes)] | ||
} else if (nodes.name) { | ||
nodes = [new AtRule(nodes)] | ||
} else if (nodes.text) { | ||
nodes = [new Comment(nodes)] | ||
} else { | ||
throw new Error('Unknown node type in node creation') | ||
} | ||
}) | ||
} | ||
let processed = nodes.map(i => { | ||
/* c8 ignore next */ | ||
if (!i[my]) Container.rebuild(i) | ||
i = i.proxyOf | ||
if (i.parent) i.parent.removeChild(i) | ||
if (i[isClean]) markDirtyUp(i) | ||
if (typeof i.raws.before === 'undefined') { | ||
if (sample && typeof sample.raws.before !== 'undefined') { | ||
i.raws.before = sample.raws.before.replace(/\S/g, '') | ||
walkDecls(prop, callback) { | ||
if (!callback) { | ||
callback = prop | ||
return this.walk((child, i) => { | ||
if (child.type === 'decl') { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
if (prop instanceof RegExp) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'decl' && prop.test(child.prop)) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
return this.walk((child, i) => { | ||
if (child.type === 'decl' && child.prop === prop) { | ||
return callback(child, i) | ||
} | ||
i.parent = this.proxyOf | ||
return i | ||
}) | ||
return processed | ||
} | ||
getProxyProcessor() { | ||
return { | ||
set(node, prop, value) { | ||
if (node[prop] === value) return true | ||
node[prop] = value | ||
if (prop === 'name' || prop === 'params' || prop === 'selector') { | ||
node.markDirty() | ||
} | ||
return true | ||
}, | ||
walkRules(selector, callback) { | ||
if (!callback) { | ||
callback = selector | ||
get(node, prop) { | ||
if (prop === 'proxyOf') { | ||
return node | ||
} else if (!node[prop]) { | ||
return node[prop] | ||
} else if ( | ||
prop === 'each' || | ||
(typeof prop === 'string' && prop.startsWith('walk')) | ||
) { | ||
return (...args) => { | ||
return node[prop]( | ||
...args.map(i => { | ||
if (typeof i === 'function') { | ||
return (child, index) => i(child.toProxy(), index) | ||
} else { | ||
return i | ||
} | ||
}) | ||
) | ||
} | ||
} else if (prop === 'every' || prop === 'some') { | ||
return cb => { | ||
return node[prop]((child, ...other) => | ||
cb(child.toProxy(), ...other) | ||
) | ||
} | ||
} else if (prop === 'root') { | ||
return () => node.root().toProxy() | ||
} else if (prop === 'nodes') { | ||
return node.nodes.map(i => i.toProxy()) | ||
} else if (prop === 'first' || prop === 'last') { | ||
return node[prop].toProxy() | ||
} else { | ||
return node[prop] | ||
return this.walk((child, i) => { | ||
if (child.type === 'rule') { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
if (selector instanceof RegExp) { | ||
return this.walk((child, i) => { | ||
if (child.type === 'rule' && selector.test(child.selector)) { | ||
return callback(child, i) | ||
} | ||
}) | ||
} | ||
return this.walk((child, i) => { | ||
if (child.type === 'rule' && child.selector === selector) { | ||
return callback(child, i) | ||
} | ||
} | ||
}) | ||
} | ||
getIterator() { | ||
if (!this.lastEach) this.lastEach = 0 | ||
if (!this.indexes) this.indexes = {} | ||
get first() { | ||
if (!this.proxyOf.nodes) return undefined | ||
return this.proxyOf.nodes[0] | ||
} | ||
this.lastEach += 1 | ||
let iterator = this.lastEach | ||
this.indexes[iterator] = 0 | ||
return iterator | ||
get last() { | ||
if (!this.proxyOf.nodes) return undefined | ||
return this.proxyOf.nodes[this.proxyOf.nodes.length - 1] | ||
} | ||
@@ -404,0 +404,0 @@ } |
import { FilePosition } from './input.js' | ||
/** | ||
* A position that is part of a range. | ||
*/ | ||
export interface RangePosition { | ||
declare namespace CssSyntaxError { | ||
/** | ||
* The line number in the input. | ||
* A position that is part of a range. | ||
*/ | ||
line: number | ||
export interface RangePosition { | ||
/** | ||
* The column number in the input. | ||
*/ | ||
column: number | ||
/** | ||
* The column number in the input. | ||
*/ | ||
column: number | ||
/** | ||
* The line number in the input. | ||
*/ | ||
line: number | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { CssSyntaxError_ as default } | ||
} | ||
@@ -47,58 +52,43 @@ | ||
*/ | ||
export default class CssSyntaxError { | ||
declare class CssSyntaxError_ { | ||
/** | ||
* Instantiates a CSS syntax error. Can be instantiated for a single position | ||
* or for a range. | ||
* @param message Error message. | ||
* @param lineOrStartPos If for a single position, the line number, or if for | ||
* a range, the inclusive start position of the error. | ||
* @param columnOrEndPos If for a single position, the column number, or if for | ||
* a range, the exclusive end position of the error. | ||
* @param source Source code of the broken file. | ||
* @param file Absolute path to the broken file. | ||
* @param plugin PostCSS plugin name, if error came from plugin. | ||
*/ | ||
constructor( | ||
message: string, | ||
lineOrStartPos?: number | RangePosition, | ||
columnOrEndPos?: number | RangePosition, | ||
source?: string, | ||
file?: string, | ||
plugin?: string | ||
) | ||
stack: string | ||
/** | ||
* Always equal to `'CssSyntaxError'`. You should always check error type | ||
* by `error.name === 'CssSyntaxError'` | ||
* instead of `error instanceof CssSyntaxError`, | ||
* because npm could have several PostCSS versions. | ||
* Source column of the error. | ||
* | ||
* ```js | ||
* if (error.name === 'CssSyntaxError') { | ||
* error //=> CssSyntaxError | ||
* } | ||
* error.column //=> 1 | ||
* error.input.column //=> 4 | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.column`. | ||
*/ | ||
name: 'CssSyntaxError' | ||
column?: number | ||
/** | ||
* Error message. | ||
* Source column of the error's end, exclusive. Provided if the error pertains | ||
* to a range. | ||
* | ||
* ```js | ||
* error.message //=> 'Unclosed block' | ||
* error.endColumn //=> 1 | ||
* error.input.endColumn //=> 4 | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.endColumn`. | ||
*/ | ||
reason: string | ||
endColumn?: number | ||
/** | ||
* Full error text in the GNU error format | ||
* with plugin, file, line and column. | ||
* Source line of the error's end, exclusive. Provided if the error pertains | ||
* to a range. | ||
* | ||
* ```js | ||
* error.message //=> 'a.css:1:1: Unclosed block' | ||
* error.endLine //=> 3 | ||
* error.input.endLine //=> 4 | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.endLine`. | ||
*/ | ||
message: string | ||
endLine?: number | ||
@@ -119,66 +109,53 @@ /** | ||
/** | ||
* Source line of the error. | ||
* Input object with PostCSS internal information | ||
* about input file. If input has source map | ||
* from previous tool, PostCSS will use origin | ||
* (for example, Sass) source. You can use this | ||
* object to get PostCSS input source. | ||
* | ||
* ```js | ||
* error.line //=> 2 | ||
* error.input.line //=> 4 | ||
* error.input.file //=> 'a.css' | ||
* error.file //=> 'a.sass' | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.line`. | ||
*/ | ||
line?: number | ||
input?: FilePosition | ||
/** | ||
* Source column of the error. | ||
* Source line of the error. | ||
* | ||
* ```js | ||
* error.column //=> 1 | ||
* error.input.column //=> 4 | ||
* error.line //=> 2 | ||
* error.input.line //=> 4 | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.column`. | ||
* If you need the position in the PostCSS input, use `error.input.line`. | ||
*/ | ||
column?: number | ||
line?: number | ||
/** | ||
* Source line of the error's end, exclusive. Provided if the error pertains | ||
* to a range. | ||
* Full error text in the GNU error format | ||
* with plugin, file, line and column. | ||
* | ||
* ```js | ||
* error.endLine //=> 3 | ||
* error.input.endLine //=> 4 | ||
* error.message //=> 'a.css:1:1: Unclosed block' | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.endLine`. | ||
*/ | ||
endLine?: number | ||
message: string | ||
/** | ||
* Source column of the error's end, exclusive. Provided if the error pertains | ||
* to a range. | ||
* Always equal to `'CssSyntaxError'`. You should always check error type | ||
* by `error.name === 'CssSyntaxError'` | ||
* instead of `error instanceof CssSyntaxError`, | ||
* because npm could have several PostCSS versions. | ||
* | ||
* ```js | ||
* error.endColumn //=> 1 | ||
* error.input.endColumn //=> 4 | ||
* if (error.name === 'CssSyntaxError') { | ||
* error //=> CssSyntaxError | ||
* } | ||
* ``` | ||
* | ||
* PostCSS will use the input source map to detect the original location. | ||
* If you need the position in the PostCSS input, use `error.input.endColumn`. | ||
*/ | ||
endColumn?: number | ||
name: 'CssSyntaxError' | ||
/** | ||
* Source code of the broken file. | ||
* | ||
* ```js | ||
* error.source //=> 'a { b {} }' | ||
* error.input.source //=> 'a b { }' | ||
* ``` | ||
*/ | ||
source?: string | ||
/** | ||
* Plugin name, if error came from plugin. | ||
@@ -193,29 +170,44 @@ * | ||
/** | ||
* Input object with PostCSS internal information | ||
* about input file. If input has source map | ||
* from previous tool, PostCSS will use origin | ||
* (for example, Sass) source. You can use this | ||
* object to get PostCSS input source. | ||
* Error message. | ||
* | ||
* ```js | ||
* error.input.file //=> 'a.css' | ||
* error.file //=> 'a.sass' | ||
* error.message //=> 'Unclosed block' | ||
* ``` | ||
*/ | ||
input?: FilePosition | ||
reason: string | ||
/** | ||
* Returns error position, message and source code of the broken part. | ||
* Source code of the broken file. | ||
* | ||
* ```js | ||
* error.toString() //=> "CssSyntaxError: app.css:1:1: Unclosed block | ||
* // > 1 | a { | ||
* // | ^" | ||
* error.source //=> 'a { b {} }' | ||
* error.input.source //=> 'a b { }' | ||
* ``` | ||
* | ||
* @return Error position, message and source code. | ||
*/ | ||
toString(): string | ||
source?: string | ||
stack: string | ||
/** | ||
* Instantiates a CSS syntax error. Can be instantiated for a single position | ||
* or for a range. | ||
* @param message Error message. | ||
* @param lineOrStartPos If for a single position, the line number, or if for | ||
* a range, the inclusive start position of the error. | ||
* @param columnOrEndPos If for a single position, the column number, or if for | ||
* a range, the exclusive end position of the error. | ||
* @param source Source code of the broken file. | ||
* @param file Absolute path to the broken file. | ||
* @param plugin PostCSS plugin name, if error came from plugin. | ||
*/ | ||
constructor( | ||
message: string, | ||
lineOrStartPos?: CssSyntaxError.RangePosition | number, | ||
columnOrEndPos?: CssSyntaxError.RangePosition | number, | ||
source?: string, | ||
file?: string, | ||
plugin?: string | ||
) | ||
/** | ||
* Returns a few lines of CSS source that caused the error. | ||
@@ -242,2 +234,19 @@ * | ||
showSourceCode(color?: boolean): string | ||
/** | ||
* Returns error position, message and source code of the broken part. | ||
* | ||
* ```js | ||
* error.toString() //=> "CssSyntaxError: app.css:1:1: Unclosed block | ||
* // > 1 | a { | ||
* // | ^" | ||
* ``` | ||
* | ||
* @return Error position, message and source code. | ||
*/ | ||
toString(): string | ||
} | ||
declare class CssSyntaxError extends CssSyntaxError_ {} | ||
export = CssSyntaxError |
@@ -67,3 +67,3 @@ 'use strict' | ||
if (color) { | ||
let { bold, red, gray } = pico.createColors(true) | ||
let { bold, gray, red } = pico.createColors(true) | ||
mark = text => bold(red(text)) | ||
@@ -70,0 +70,0 @@ aside = text => gray(text) |
import Container from './container.js' | ||
import Node from './node.js' | ||
interface DeclarationRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
*/ | ||
before?: string | ||
declare namespace Declaration { | ||
export interface DeclarationRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
*/ | ||
before?: string | ||
/** | ||
* The symbols between the property and value for declarations. | ||
*/ | ||
between?: string | ||
/** | ||
* The symbols between the property and value for declarations. | ||
*/ | ||
between?: string | ||
/** | ||
* The content of the important statement, if it is not just `!important`. | ||
*/ | ||
important?: string | ||
/** | ||
* The content of the important statement, if it is not just `!important`. | ||
*/ | ||
important?: string | ||
/** | ||
* Declaration value with comments. | ||
*/ | ||
value?: { | ||
/** | ||
* Declaration value with comments. | ||
*/ | ||
value?: { | ||
raw: string | ||
value: string | ||
} | ||
} | ||
export interface DeclarationProps { | ||
/** Whether the declaration has an `!important` annotation. */ | ||
important?: boolean | ||
/** Name of the declaration. */ | ||
prop: string | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: DeclarationRaws | ||
/** Value of the declaration. */ | ||
value: string | ||
raw: string | ||
} | ||
} | ||
export interface DeclarationProps { | ||
/** Name of the declaration. */ | ||
prop: string | ||
/** Value of the declaration. */ | ||
value: string | ||
/** Whether the declaration has an `!important` annotation. */ | ||
important?: boolean | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: DeclarationRaws | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Declaration_ as default } | ||
} | ||
/** | ||
* Represents a CSS declaration. | ||
* It represents a class that handles | ||
* [CSS declarations](https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax#css_declarations) | ||
* | ||
* ```js | ||
* Once (root, { Declaration }) { | ||
* let color = new Declaration({ prop: 'color', value: 'black' }) | ||
* const color = new Declaration({ prop: 'color', value: 'black' }) | ||
* root.append(color) | ||
@@ -53,3 +59,4 @@ * } | ||
* const root = postcss.parse('a { color: black }') | ||
* const decl = root.first.first | ||
* const decl = root.first?.first | ||
* | ||
* decl.type //=> 'decl' | ||
@@ -59,9 +66,23 @@ * decl.toString() //=> ' color: black' | ||
*/ | ||
export default class Declaration extends Node { | ||
type: 'decl' | ||
declare class Declaration_ extends Node { | ||
/** | ||
* It represents a specificity of the declaration. | ||
* | ||
* If true, the CSS declaration will have an | ||
* [important](https://developer.mozilla.org/en-US/docs/Web/CSS/important) | ||
* specifier. | ||
* | ||
* ```js | ||
* const root = postcss.parse('a { color: black !important; color: red }') | ||
* | ||
* root.first.first.important //=> true | ||
* root.first.last.important //=> undefined | ||
* ``` | ||
*/ | ||
important: boolean | ||
parent: Container | undefined | ||
raws: DeclarationRaws | ||
/** | ||
* The declaration's property name. | ||
* The property name for a CSS declaration. | ||
* | ||
@@ -71,2 +92,3 @@ * ```js | ||
* const decl = root.first.first | ||
* | ||
* decl.prop //=> 'color' | ||
@@ -77,13 +99,20 @@ * ``` | ||
raws: Declaration.DeclarationRaws | ||
type: 'decl' | ||
/** | ||
* The declaration’s value. | ||
* The property value for a CSS declaration. | ||
* | ||
* This value will be cleaned of comments. If the source value contained | ||
* comments, those comments will be available in the `raws` property. | ||
* If you have not changed the value, the result of `decl.toString()` | ||
* will include the original raws value (comments and all). | ||
* Any CSS comments inside the value string will be filtered out. | ||
* CSS comments present in the source value will be available in | ||
* the `raws` property. | ||
* | ||
* Assigning new `value` would ignore the comments in `raws` | ||
* property while compiling node to string. | ||
* | ||
* ```js | ||
* const root = postcss.parse('a { color: black }') | ||
* const decl = root.first.first | ||
* | ||
* decl.value //=> 'black' | ||
@@ -95,19 +124,9 @@ * ``` | ||
/** | ||
* `true` if the declaration has an `!important` annotation. | ||
* It represents a getter that returns `true` if a declaration starts with | ||
* `--` or `$`, which are used to declare variables in CSS and SASS/SCSS. | ||
* | ||
* ```js | ||
* const root = postcss.parse('a { color: black !important; color: red }') | ||
* root.first.first.important //=> true | ||
* root.first.last.important //=> undefined | ||
* ``` | ||
*/ | ||
important: boolean | ||
/** | ||
* `true` if declaration is declaration of CSS Custom Property | ||
* or Sass variable. | ||
* const root = postcss.parse(':root { --one: 1 }') | ||
* const one = root.first.first | ||
* | ||
* ```js | ||
* const root = postcss.parse(':root { --one: 1 }') | ||
* let one = root.first.first | ||
* one.variable //=> true | ||
@@ -118,3 +137,4 @@ * ``` | ||
* const root = postcss.parse('$one: 1') | ||
* let one = root.first | ||
* const one = root.first | ||
* | ||
* one.variable //=> true | ||
@@ -125,7 +145,11 @@ * ``` | ||
constructor(defaults?: DeclarationProps) | ||
assign(overrides: object | DeclarationProps): this | ||
clone(overrides?: Partial<DeclarationProps>): this | ||
cloneBefore(overrides?: Partial<DeclarationProps>): this | ||
cloneAfter(overrides?: Partial<DeclarationProps>): this | ||
constructor(defaults?: Declaration.DeclarationProps) | ||
assign(overrides: Declaration.DeclarationProps | object): this | ||
clone(overrides?: Partial<Declaration.DeclarationProps>): Declaration | ||
cloneAfter(overrides?: Partial<Declaration.DeclarationProps>): Declaration | ||
cloneBefore(overrides?: Partial<Declaration.DeclarationProps>): Declaration | ||
} | ||
declare class Declaration extends Declaration_ {} | ||
export = Declaration |
import Container, { ContainerProps } from './container.js' | ||
import { ProcessOptions } from './postcss.js' | ||
import Result from './result.js' | ||
import Root, { RootProps } from './root.js' | ||
import Root from './root.js' | ||
export interface DocumentProps extends ContainerProps { | ||
nodes?: Root[] | ||
declare namespace Document { | ||
export interface DocumentProps extends ContainerProps { | ||
nodes?: Root[] | ||
/** | ||
* Information to generate byte-to-byte equal node string as it was | ||
* in the origin input. | ||
* | ||
* Every parser saves its own properties. | ||
*/ | ||
raws?: Record<string, any> | ||
/** | ||
* Information to generate byte-to-byte equal node string as it was | ||
* in the origin input. | ||
* | ||
* Every parser saves its own properties. | ||
*/ | ||
raws?: Record<string, any> | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Document_ as default } | ||
} | ||
type ChildNode = Root | ||
type ChildProps = RootProps | ||
/** | ||
@@ -35,8 +37,13 @@ * Represents a file and contains all its parsed nodes. | ||
*/ | ||
export default class Document extends Container<Root> { | ||
declare class Document_ extends Container<Root> { | ||
parent: undefined | ||
type: 'document' | ||
parent: undefined | ||
constructor(defaults?: DocumentProps) | ||
constructor(defaults?: Document.DocumentProps) | ||
assign(overrides: Document.DocumentProps | object): this | ||
clone(overrides?: Partial<Document.DocumentProps>): Document | ||
cloneAfter(overrides?: Partial<Document.DocumentProps>): Document | ||
cloneBefore(overrides?: Partial<Document.DocumentProps>): Document | ||
/** | ||
@@ -59,1 +66,5 @@ * Returns a `Result` instance representing the document’s CSS roots. | ||
} | ||
declare class Document extends Document_ {} | ||
export = Document |
import { JSONHydrator } from './postcss.js' | ||
declare const fromJSON: JSONHydrator | ||
interface FromJSON extends JSONHydrator { | ||
default: FromJSON | ||
} | ||
export default fromJSON | ||
declare const fromJSON: FromJSON | ||
export = fromJSON |
@@ -1,39 +0,44 @@ | ||
import { ProcessOptions } from './postcss.js' | ||
import { CssSyntaxError, ProcessOptions } from './postcss.js' | ||
import PreviousMap from './previous-map.js' | ||
export interface FilePosition { | ||
/** | ||
* URL for the source file. | ||
*/ | ||
url: string | ||
declare namespace Input { | ||
export interface FilePosition { | ||
/** | ||
* Column of inclusive start position in source file. | ||
*/ | ||
column: number | ||
/** | ||
* Absolute path to the source file. | ||
*/ | ||
file?: string | ||
/** | ||
* Column of exclusive end position in source file. | ||
*/ | ||
endColumn?: number | ||
/** | ||
* Line of inclusive start position in source file. | ||
*/ | ||
line: number | ||
/** | ||
* Line of exclusive end position in source file. | ||
*/ | ||
endLine?: number | ||
/** | ||
* Column of inclusive start position in source file. | ||
*/ | ||
column: number | ||
/** | ||
* Absolute path to the source file. | ||
*/ | ||
file?: string | ||
/** | ||
* Line of exclusive end position in source file. | ||
*/ | ||
endLine?: number | ||
/** | ||
* Line of inclusive start position in source file. | ||
*/ | ||
line: number | ||
/** | ||
* Column of exclusive end position in source file. | ||
*/ | ||
endColumn?: number | ||
/** | ||
* Source code. | ||
*/ | ||
source?: string | ||
/** | ||
* Source code. | ||
*/ | ||
source?: string | ||
/** | ||
* URL for the source file. | ||
*/ | ||
url: string | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Input_ as default } | ||
} | ||
@@ -49,3 +54,3 @@ | ||
*/ | ||
export default class Input { | ||
declare class Input_ { | ||
/** | ||
@@ -62,12 +67,2 @@ * Input CSS source. | ||
/** | ||
* The input source map passed from a compilation step before PostCSS | ||
* (for example, from Sass compiler). | ||
* | ||
* ```js | ||
* root.source.input.map.consumer().sources //=> ['a.sass'] | ||
* ``` | ||
*/ | ||
map: PreviousMap | ||
/** | ||
* The absolute path to the CSS source file defined | ||
@@ -84,2 +79,7 @@ * with the `from` option. | ||
/** | ||
* The flag to indicate whether or not the source code has Unicode BOM. | ||
*/ | ||
hasBOM: boolean | ||
/** | ||
* The unique ID of the CSS source. It will be created if `from` option | ||
@@ -97,5 +97,10 @@ * is not provided (because PostCSS does not know the file path). | ||
/** | ||
* The flag to indicate whether or not the source code has Unicode BOM. | ||
* The input source map passed from a compilation step before PostCSS | ||
* (for example, from Sass compiler). | ||
* | ||
* ```js | ||
* root.source.input.map.consumer().sources //=> ['a.sass'] | ||
* ``` | ||
*/ | ||
hasBOM: boolean | ||
map: PreviousMap | ||
@@ -108,17 +113,46 @@ /** | ||
error( | ||
message: string, | ||
start: | ||
| { | ||
column: number | ||
line: number | ||
} | ||
| { | ||
offset: number | ||
}, | ||
end: | ||
| { | ||
column: number | ||
line: number | ||
} | ||
| { | ||
offset: number | ||
}, | ||
opts?: { plugin?: CssSyntaxError['plugin'] } | ||
): CssSyntaxError | ||
/** | ||
* The CSS source identifier. Contains `Input#file` if the user | ||
* set the `from` option, or `Input#id` if they did not. | ||
* | ||
* ```js | ||
* const root = postcss.parse(css, { from: 'a.css' }) | ||
* root.source.input.from //=> "/home/ai/a.css" | ||
* | ||
* const root = postcss.parse(css) | ||
* root.source.input.from //=> "<input css 1>" | ||
* ``` | ||
* Returns `CssSyntaxError` with information about the error and its position. | ||
*/ | ||
get from(): string | ||
error( | ||
message: string, | ||
line: number, | ||
column: number, | ||
opts?: { plugin?: CssSyntaxError['plugin'] } | ||
): CssSyntaxError | ||
error( | ||
message: string, | ||
offset: number, | ||
opts?: { plugin?: CssSyntaxError['plugin'] } | ||
): CssSyntaxError | ||
/** | ||
* Converts source offset to line and column. | ||
* | ||
* @param offset Source offset. | ||
*/ | ||
fromOffset(offset: number): { col: number; line: number } | null | ||
/** | ||
* Reads the input source map and returns a symbol position | ||
@@ -147,10 +181,20 @@ * in the input source (e.g., in a Sass file that was compiled | ||
endColumn?: number | ||
): FilePosition | false | ||
): false | Input.FilePosition | ||
/** | ||
* Converts source offset to line and column. | ||
* The CSS source identifier. Contains `Input#file` if the user | ||
* set the `from` option, or `Input#id` if they did not. | ||
* | ||
* @param offset Source offset. | ||
* ```js | ||
* const root = postcss.parse(css, { from: 'a.css' }) | ||
* root.source.input.from //=> "/home/ai/a.css" | ||
* | ||
* const root = postcss.parse(css) | ||
* root.source.input.from //=> "<input css 1>" | ||
* ``` | ||
*/ | ||
fromOffset(offset: number): { line: number; col: number } | null | ||
get from(): string | ||
} | ||
declare class Input extends Input_ {} | ||
export = Input |
130
lib/input.js
@@ -5,3 +5,3 @@ 'use strict' | ||
let { fileURLToPath, pathToFileURL } = require('url') | ||
let { resolve, isAbsolute } = require('path') | ||
let { isAbsolute, resolve } = require('path') | ||
let { nanoid } = require('nanoid/non-secure') | ||
@@ -64,44 +64,2 @@ | ||
fromOffset(offset) { | ||
let lastLine, lineToIndex | ||
if (!this[fromOffsetCache]) { | ||
let lines = this.css.split('\n') | ||
lineToIndex = new Array(lines.length) | ||
let prevIndex = 0 | ||
for (let i = 0, l = lines.length; i < l; i++) { | ||
lineToIndex[i] = prevIndex | ||
prevIndex += lines[i].length + 1 | ||
} | ||
this[fromOffsetCache] = lineToIndex | ||
} else { | ||
lineToIndex = this[fromOffsetCache] | ||
} | ||
lastLine = lineToIndex[lineToIndex.length - 1] | ||
let min = 0 | ||
if (offset >= lastLine) { | ||
min = lineToIndex.length - 1 | ||
} else { | ||
let max = lineToIndex.length - 2 | ||
let mid | ||
while (min < max) { | ||
mid = min + ((max - min) >> 1) | ||
if (offset < lineToIndex[mid]) { | ||
max = mid - 1 | ||
} else if (offset >= lineToIndex[mid + 1]) { | ||
min = mid + 1 | ||
} else { | ||
min = mid | ||
break | ||
} | ||
} | ||
} | ||
return { | ||
line: min + 1, | ||
col: offset - lineToIndex[min] + 1 | ||
} | ||
} | ||
error(message, line, column, opts = {}) { | ||
@@ -113,3 +71,3 @@ let result, endLine, endColumn | ||
let end = column | ||
if (typeof line.offset === 'number') { | ||
if (typeof start.offset === 'number') { | ||
let pos = this.fromOffset(start.offset) | ||
@@ -142,6 +100,6 @@ line = pos.line | ||
? origin.line | ||
: { line: origin.line, column: origin.column }, | ||
: { column: origin.column, line: origin.line }, | ||
origin.endLine === undefined | ||
? origin.column | ||
: { line: origin.endLine, column: origin.endColumn }, | ||
: { column: origin.endColumn, line: origin.endLine }, | ||
origin.source, | ||
@@ -154,4 +112,4 @@ origin.file, | ||
message, | ||
endLine === undefined ? line : { line, column }, | ||
endLine === undefined ? column : { line: endLine, column: endColumn }, | ||
endLine === undefined ? line : { column, line }, | ||
endLine === undefined ? column : { column: endColumn, line: endLine }, | ||
this.css, | ||
@@ -163,3 +121,3 @@ this.file, | ||
result.input = { line, column, endLine, endColumn, source: this.css } | ||
result.input = { column, endColumn, endLine, line, source: this.css } | ||
if (this.file) { | ||
@@ -175,2 +133,51 @@ if (pathToFileURL) { | ||
fromOffset(offset) { | ||
let lastLine, lineToIndex | ||
if (!this[fromOffsetCache]) { | ||
let lines = this.css.split('\n') | ||
lineToIndex = new Array(lines.length) | ||
let prevIndex = 0 | ||
for (let i = 0, l = lines.length; i < l; i++) { | ||
lineToIndex[i] = prevIndex | ||
prevIndex += lines[i].length + 1 | ||
} | ||
this[fromOffsetCache] = lineToIndex | ||
} else { | ||
lineToIndex = this[fromOffsetCache] | ||
} | ||
lastLine = lineToIndex[lineToIndex.length - 1] | ||
let min = 0 | ||
if (offset >= lastLine) { | ||
min = lineToIndex.length - 1 | ||
} else { | ||
let max = lineToIndex.length - 2 | ||
let mid | ||
while (min < max) { | ||
mid = min + ((max - min) >> 1) | ||
if (offset < lineToIndex[mid]) { | ||
max = mid - 1 | ||
} else if (offset >= lineToIndex[mid + 1]) { | ||
min = mid + 1 | ||
} else { | ||
min = mid | ||
break | ||
} | ||
} | ||
} | ||
return { | ||
col: offset - lineToIndex[min] + 1, | ||
line: min + 1 | ||
} | ||
} | ||
mapResolve(file) { | ||
if (/^\w+:\/\//.test(file)) { | ||
return file | ||
} | ||
return resolve(this.map.consumer().sourceRoot || this.map.root || '.', file) | ||
} | ||
origin(line, column, endLine, endColumn) { | ||
@@ -180,3 +187,3 @@ if (!this.map) return false | ||
let from = consumer.originalPositionFor({ line, column }) | ||
let from = consumer.originalPositionFor({ column, line }) | ||
if (!from.source) return false | ||
@@ -186,3 +193,3 @@ | ||
if (typeof endLine === 'number') { | ||
to = consumer.originalPositionFor({ line: endLine, column: endColumn }) | ||
to = consumer.originalPositionFor({ column: endColumn, line: endLine }) | ||
} | ||
@@ -202,7 +209,7 @@ | ||
let result = { | ||
url: fromUrl.toString(), | ||
line: from.line, | ||
column: from.column, | ||
endColumn: to && to.column, | ||
endLine: to && to.line, | ||
endColumn: to && to.column | ||
line: from.line, | ||
url: fromUrl.toString() | ||
} | ||
@@ -225,13 +232,2 @@ | ||
mapResolve(file) { | ||
if (/^\w+:\/\//.test(file)) { | ||
return file | ||
} | ||
return resolve(this.map.consumer().sourceRoot || this.map.root || '.', file) | ||
} | ||
get from() { | ||
return this.file || this.id | ||
} | ||
toJSON() { | ||
@@ -252,2 +248,6 @@ let json = {} | ||
} | ||
get from() { | ||
return this.file || this.id | ||
} | ||
} | ||
@@ -254,0 +254,0 @@ |
@@ -1,7 +0,13 @@ | ||
import Result, { Message, ResultOptions } from './result.js' | ||
import Document from './document.js' | ||
import { SourceMap } from './postcss.js' | ||
import Processor from './processor.js' | ||
import Result, { Message, ResultOptions } from './result.js' | ||
import Root from './root.js' | ||
import Warning from './warning.js' | ||
import Root from './root.js' | ||
declare namespace LazyResult { | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { LazyResult_ as default } | ||
} | ||
/** | ||
@@ -16,7 +22,8 @@ * A Promise proxy for the result of PostCSS transformations. | ||
*/ | ||
export default class LazyResult implements PromiseLike<Result> { | ||
declare class LazyResult_<RootNode = Document | Root> | ||
implements PromiseLike<Result<RootNode>> | ||
{ | ||
/** | ||
* Processes input CSS through synchronous and asynchronous plugins | ||
* and calls `onFulfilled` with a Result instance. If a plugin throws | ||
* an error, the `onRejected` callback will be executed. | ||
* and calls onRejected for each error thrown in any plugin. | ||
* | ||
@@ -26,12 +33,14 @@ * It implements standard Promise API. | ||
* ```js | ||
* postcss([autoprefixer]).process(css, { from: cssPath }).then(result => { | ||
* postcss([autoprefixer]).process(css).then(result => { | ||
* console.log(result.css) | ||
* }).catch(error => { | ||
* console.error(error) | ||
* }) | ||
* ``` | ||
*/ | ||
then: Promise<Result>['then'] | ||
catch: Promise<Result<RootNode>>['catch'] | ||
/** | ||
* Processes input CSS through synchronous and asynchronous plugins | ||
* and calls onRejected for each error thrown in any plugin. | ||
* and calls onFinally on any error or when all plugins will finish work. | ||
* | ||
@@ -41,14 +50,13 @@ * It implements standard Promise API. | ||
* ```js | ||
* postcss([autoprefixer]).process(css).then(result => { | ||
* console.log(result.css) | ||
* }).catch(error => { | ||
* console.error(error) | ||
* postcss([autoprefixer]).process(css).finally(() => { | ||
* console.log('processing ended') | ||
* }) | ||
* ``` | ||
*/ | ||
catch: Promise<Result>['catch'] | ||
finally: Promise<Result<RootNode>>['finally'] | ||
/** | ||
* Processes input CSS through synchronous and asynchronous plugins | ||
* and calls onFinally on any error or when all plugins will finish work. | ||
* and calls `onFulfilled` with a Result instance. If a plugin throws | ||
* an error, the `onRejected` callback will be executed. | ||
* | ||
@@ -58,8 +66,8 @@ * It implements standard Promise API. | ||
* ```js | ||
* postcss([autoprefixer]).process(css).finally(() => { | ||
* console.log('processing ended') | ||
* postcss([autoprefixer]).process(css, { from: cssPath }).then(result => { | ||
* console.log(result.css) | ||
* }) | ||
* ``` | ||
*/ | ||
finally: Promise<Result>['finally'] | ||
then: Promise<Result<RootNode>>['then'] | ||
@@ -74,29 +82,33 @@ /** | ||
/** | ||
* Returns the default string description of an object. | ||
* Required to implement the Promise interface. | ||
* Run plugin in async way and return `Result`. | ||
* | ||
* @return Result with output content. | ||
*/ | ||
get [Symbol.toStringTag](): string | ||
async(): Promise<Result<RootNode>> | ||
/** | ||
* Returns a `Processor` instance, which will be used | ||
* for CSS transformations. | ||
* Run plugin in sync way and return `Result`. | ||
* | ||
* @return Result with output content. | ||
*/ | ||
get processor(): Processor | ||
sync(): Result<RootNode> | ||
/** | ||
* Options from the `Processor#process` call. | ||
* Alias for the `LazyResult#css` property. | ||
* | ||
* ```js | ||
* lazy + '' === lazy.css | ||
* ``` | ||
* | ||
* @return Output CSS. | ||
*/ | ||
get opts(): ResultOptions | ||
toString(): string | ||
/** | ||
* Processes input CSS through synchronous plugins, converts `Root` | ||
* to a CSS string and returns `Result#css`. | ||
* Processes input CSS through synchronous plugins | ||
* and calls `Result#warnings`. | ||
* | ||
* This property will only work with synchronous plugins. | ||
* If the processor contains any asynchronous plugins | ||
* it will throw an error. | ||
* | ||
* PostCSS runners should always use `LazyResult#then`. | ||
* @return Warnings from plugins. | ||
*/ | ||
get css(): string | ||
warnings(): Warning[] | ||
@@ -116,4 +128,4 @@ /** | ||
/** | ||
* Processes input CSS through synchronous plugins | ||
* and returns `Result#map`. | ||
* Processes input CSS through synchronous plugins, converts `Root` | ||
* to a CSS string and returns `Result#css`. | ||
* | ||
@@ -126,14 +138,15 @@ * This property will only work with synchronous plugins. | ||
*/ | ||
get map(): SourceMap | ||
get css(): string | ||
/** | ||
* Processes input CSS through synchronous plugins | ||
* and returns `Result#root`. | ||
* and returns `Result#map`. | ||
* | ||
* This property will only work with synchronous plugins. If the processor | ||
* contains any asynchronous plugins it will throw an error. | ||
* This property will only work with synchronous plugins. | ||
* If the processor contains any asynchronous plugins | ||
* it will throw an error. | ||
* | ||
* PostCSS runners should always use `LazyResult#then`. | ||
*/ | ||
get root(): Root | ||
get map(): SourceMap | ||
@@ -152,33 +165,34 @@ /** | ||
/** | ||
* Processes input CSS through synchronous plugins | ||
* and calls `Result#warnings`. | ||
* | ||
* @return Warnings from plugins. | ||
* Options from the `Processor#process` call. | ||
*/ | ||
warnings(): Warning[] | ||
get opts(): ResultOptions | ||
/** | ||
* Alias for the `LazyResult#css` property. | ||
* | ||
* ```js | ||
* lazy + '' === lazy.css | ||
* ``` | ||
* | ||
* @return Output CSS. | ||
* Returns a `Processor` instance, which will be used | ||
* for CSS transformations. | ||
*/ | ||
toString(): string | ||
get processor(): Processor | ||
/** | ||
* Run plugin in sync way and return `Result`. | ||
* Processes input CSS through synchronous plugins | ||
* and returns `Result#root`. | ||
* | ||
* @return Result with output content. | ||
* This property will only work with synchronous plugins. If the processor | ||
* contains any asynchronous plugins it will throw an error. | ||
* | ||
* PostCSS runners should always use `LazyResult#then`. | ||
*/ | ||
sync(): Result | ||
get root(): RootNode | ||
/** | ||
* Run plugin in async way and return `Result`. | ||
* | ||
* @return Result with output content. | ||
* Returns the default string description of an object. | ||
* Required to implement the Promise interface. | ||
*/ | ||
async(): Promise<Result> | ||
get [Symbol.toStringTag](): string | ||
} | ||
declare class LazyResult< | ||
RootNode = Document | Root | ||
> extends LazyResult_<RootNode> {} | ||
export = LazyResult |
@@ -14,33 +14,33 @@ 'use strict' | ||
const TYPE_TO_CLASS_NAME = { | ||
atrule: 'AtRule', | ||
comment: 'Comment', | ||
decl: 'Declaration', | ||
document: 'Document', | ||
root: 'Root', | ||
atrule: 'AtRule', | ||
rule: 'Rule', | ||
decl: 'Declaration', | ||
comment: 'Comment' | ||
rule: 'Rule' | ||
} | ||
const PLUGIN_PROPS = { | ||
AtRule: true, | ||
AtRuleExit: true, | ||
Comment: true, | ||
CommentExit: true, | ||
Declaration: true, | ||
DeclarationExit: true, | ||
Document: true, | ||
DocumentExit: true, | ||
Once: true, | ||
OnceExit: true, | ||
postcssPlugin: true, | ||
prepare: true, | ||
Once: true, | ||
Document: true, | ||
Root: true, | ||
Declaration: true, | ||
RootExit: true, | ||
Rule: true, | ||
AtRule: true, | ||
Comment: true, | ||
DeclarationExit: true, | ||
RuleExit: true, | ||
AtRuleExit: true, | ||
CommentExit: true, | ||
RootExit: true, | ||
DocumentExit: true, | ||
OnceExit: true | ||
RuleExit: true | ||
} | ||
const NOT_VISITORS = { | ||
Once: true, | ||
postcssPlugin: true, | ||
prepare: true, | ||
Once: true | ||
prepare: true | ||
} | ||
@@ -91,8 +91,8 @@ | ||
return { | ||
eventIndex: 0, | ||
events, | ||
iterator: 0, | ||
node, | ||
events, | ||
eventIndex: 0, | ||
visitors: [], | ||
visitorIndex: 0, | ||
iterator: 0 | ||
visitors: [] | ||
} | ||
@@ -148,3 +148,3 @@ } | ||
this.result = new Result(processor, root, opts) | ||
this.helpers = { ...postcss, result: this.result, postcss } | ||
this.helpers = { ...postcss, postcss, result: this.result } | ||
this.plugins = this.processor.plugins.map(plugin => { | ||
@@ -159,63 +159,2 @@ if (typeof plugin === 'object' && plugin.prepare) { | ||
get [Symbol.toStringTag]() { | ||
return 'LazyResult' | ||
} | ||
get processor() { | ||
return this.result.processor | ||
} | ||
get opts() { | ||
return this.result.opts | ||
} | ||
get css() { | ||
return this.stringify().css | ||
} | ||
get content() { | ||
return this.stringify().content | ||
} | ||
get map() { | ||
return this.stringify().map | ||
} | ||
get root() { | ||
return this.sync().root | ||
} | ||
get messages() { | ||
return this.sync().messages | ||
} | ||
warnings() { | ||
return this.sync().warnings() | ||
} | ||
toString() { | ||
return this.css | ||
} | ||
then(onFulfilled, onRejected) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (!('from' in this.opts)) { | ||
warnOnce( | ||
'Without `from` option PostCSS could generate wrong source map ' + | ||
'and will not find Browserslist config. Set it to CSS file path ' + | ||
'or to `undefined` to prevent this warning.' | ||
) | ||
} | ||
} | ||
return this.async().then(onFulfilled, onRejected) | ||
} | ||
catch(onRejected) { | ||
return this.async().catch(onRejected) | ||
} | ||
finally(onFinally) { | ||
return this.async().then(onFinally, onFinally) | ||
} | ||
async() { | ||
@@ -230,122 +169,10 @@ if (this.error) return Promise.reject(this.error) | ||
sync() { | ||
if (this.error) throw this.error | ||
if (this.processed) return this.result | ||
this.processed = true | ||
if (this.processing) { | ||
throw this.getAsyncError() | ||
} | ||
for (let plugin of this.plugins) { | ||
let promise = this.runOnRoot(plugin) | ||
if (isPromise(promise)) { | ||
throw this.getAsyncError() | ||
} | ||
} | ||
this.prepareVisitors() | ||
if (this.hasListener) { | ||
let root = this.result.root | ||
while (!root[isClean]) { | ||
root[isClean] = true | ||
this.walkSync(root) | ||
} | ||
if (this.listeners.OnceExit) { | ||
if (root.type === 'document') { | ||
for (let subRoot of root.nodes) { | ||
this.visitSync(this.listeners.OnceExit, subRoot) | ||
} | ||
} else { | ||
this.visitSync(this.listeners.OnceExit, root) | ||
} | ||
} | ||
} | ||
return this.result | ||
catch(onRejected) { | ||
return this.async().catch(onRejected) | ||
} | ||
stringify() { | ||
if (this.error) throw this.error | ||
if (this.stringified) return this.result | ||
this.stringified = true | ||
this.sync() | ||
let opts = this.result.opts | ||
let str = stringify | ||
if (opts.syntax) str = opts.syntax.stringify | ||
if (opts.stringifier) str = opts.stringifier | ||
if (str.stringify) str = str.stringify | ||
let map = new MapGenerator(str, this.result.root, this.result.opts) | ||
let data = map.generate() | ||
this.result.css = data[0] | ||
this.result.map = data[1] | ||
return this.result | ||
finally(onFinally) { | ||
return this.async().then(onFinally, onFinally) | ||
} | ||
walkSync(node) { | ||
node[isClean] = true | ||
let events = getEvents(node) | ||
for (let event of events) { | ||
if (event === CHILDREN) { | ||
if (node.nodes) { | ||
node.each(child => { | ||
if (!child[isClean]) this.walkSync(child) | ||
}) | ||
} | ||
} else { | ||
let visitors = this.listeners[event] | ||
if (visitors) { | ||
if (this.visitSync(visitors, node.toProxy())) return | ||
} | ||
} | ||
} | ||
} | ||
visitSync(visitors, node) { | ||
for (let [plugin, visitor] of visitors) { | ||
this.result.lastPlugin = plugin | ||
let promise | ||
try { | ||
promise = visitor(node, this.helpers) | ||
} catch (e) { | ||
throw this.handleError(e, node.proxyOf) | ||
} | ||
if (node.type !== 'root' && node.type !== 'document' && !node.parent) { | ||
return true | ||
} | ||
if (isPromise(promise)) { | ||
throw this.getAsyncError() | ||
} | ||
} | ||
} | ||
runOnRoot(plugin) { | ||
this.result.lastPlugin = plugin | ||
try { | ||
if (typeof plugin === 'object' && plugin.Once) { | ||
if (this.result.root.type === 'document') { | ||
let roots = this.result.root.nodes.map(root => | ||
plugin.Once(root, this.helpers) | ||
) | ||
if (isPromise(roots[0])) { | ||
return Promise.all(roots) | ||
} | ||
return roots | ||
} | ||
return plugin.Once(this.result.root, this.helpers) | ||
} else if (typeof plugin === 'function') { | ||
return plugin(this.result.root, this.result) | ||
} | ||
} catch (error) { | ||
throw this.handleError(error) | ||
} | ||
} | ||
getAsyncError() { | ||
@@ -394,2 +221,40 @@ throw new Error('Use process(css).then(cb) to work with async plugins') | ||
prepareVisitors() { | ||
this.listeners = {} | ||
let add = (plugin, type, cb) => { | ||
if (!this.listeners[type]) this.listeners[type] = [] | ||
this.listeners[type].push([plugin, cb]) | ||
} | ||
for (let plugin of this.plugins) { | ||
if (typeof plugin === 'object') { | ||
for (let event in plugin) { | ||
if (!PLUGIN_PROPS[event] && /^[A-Z]/.test(event)) { | ||
throw new Error( | ||
`Unknown event ${event} in ${plugin.postcssPlugin}. ` + | ||
`Try to update PostCSS (${this.processor.version} now).` | ||
) | ||
} | ||
if (!NOT_VISITORS[event]) { | ||
if (typeof plugin[event] === 'object') { | ||
for (let filter in plugin[event]) { | ||
if (filter === '*') { | ||
add(plugin, event, plugin[event][filter]) | ||
} else { | ||
add( | ||
plugin, | ||
event + '-' + filter.toLowerCase(), | ||
plugin[event][filter] | ||
) | ||
} | ||
} | ||
} else if (typeof plugin[event] === 'function') { | ||
add(plugin, event, plugin[event]) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
this.hasListener = Object.keys(this.listeners).length > 0 | ||
} | ||
async runAsync() { | ||
@@ -452,40 +317,120 @@ this.plugin = 0 | ||
prepareVisitors() { | ||
this.listeners = {} | ||
let add = (plugin, type, cb) => { | ||
if (!this.listeners[type]) this.listeners[type] = [] | ||
this.listeners[type].push([plugin, cb]) | ||
runOnRoot(plugin) { | ||
this.result.lastPlugin = plugin | ||
try { | ||
if (typeof plugin === 'object' && plugin.Once) { | ||
if (this.result.root.type === 'document') { | ||
let roots = this.result.root.nodes.map(root => | ||
plugin.Once(root, this.helpers) | ||
) | ||
if (isPromise(roots[0])) { | ||
return Promise.all(roots) | ||
} | ||
return roots | ||
} | ||
return plugin.Once(this.result.root, this.helpers) | ||
} else if (typeof plugin === 'function') { | ||
return plugin(this.result.root, this.result) | ||
} | ||
} catch (error) { | ||
throw this.handleError(error) | ||
} | ||
} | ||
stringify() { | ||
if (this.error) throw this.error | ||
if (this.stringified) return this.result | ||
this.stringified = true | ||
this.sync() | ||
let opts = this.result.opts | ||
let str = stringify | ||
if (opts.syntax) str = opts.syntax.stringify | ||
if (opts.stringifier) str = opts.stringifier | ||
if (str.stringify) str = str.stringify | ||
let map = new MapGenerator(str, this.result.root, this.result.opts) | ||
let data = map.generate() | ||
this.result.css = data[0] | ||
this.result.map = data[1] | ||
return this.result | ||
} | ||
sync() { | ||
if (this.error) throw this.error | ||
if (this.processed) return this.result | ||
this.processed = true | ||
if (this.processing) { | ||
throw this.getAsyncError() | ||
} | ||
for (let plugin of this.plugins) { | ||
if (typeof plugin === 'object') { | ||
for (let event in plugin) { | ||
if (!PLUGIN_PROPS[event] && /^[A-Z]/.test(event)) { | ||
throw new Error( | ||
`Unknown event ${event} in ${plugin.postcssPlugin}. ` + | ||
`Try to update PostCSS (${this.processor.version} now).` | ||
) | ||
let promise = this.runOnRoot(plugin) | ||
if (isPromise(promise)) { | ||
throw this.getAsyncError() | ||
} | ||
} | ||
this.prepareVisitors() | ||
if (this.hasListener) { | ||
let root = this.result.root | ||
while (!root[isClean]) { | ||
root[isClean] = true | ||
this.walkSync(root) | ||
} | ||
if (this.listeners.OnceExit) { | ||
if (root.type === 'document') { | ||
for (let subRoot of root.nodes) { | ||
this.visitSync(this.listeners.OnceExit, subRoot) | ||
} | ||
if (!NOT_VISITORS[event]) { | ||
if (typeof plugin[event] === 'object') { | ||
for (let filter in plugin[event]) { | ||
if (filter === '*') { | ||
add(plugin, event, plugin[event][filter]) | ||
} else { | ||
add( | ||
plugin, | ||
event + '-' + filter.toLowerCase(), | ||
plugin[event][filter] | ||
) | ||
} | ||
} | ||
} else if (typeof plugin[event] === 'function') { | ||
add(plugin, event, plugin[event]) | ||
} | ||
} | ||
} else { | ||
this.visitSync(this.listeners.OnceExit, root) | ||
} | ||
} | ||
} | ||
this.hasListener = Object.keys(this.listeners).length > 0 | ||
return this.result | ||
} | ||
then(onFulfilled, onRejected) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (!('from' in this.opts)) { | ||
warnOnce( | ||
'Without `from` option PostCSS could generate wrong source map ' + | ||
'and will not find Browserslist config. Set it to CSS file path ' + | ||
'or to `undefined` to prevent this warning.' | ||
) | ||
} | ||
} | ||
return this.async().then(onFulfilled, onRejected) | ||
} | ||
toString() { | ||
return this.css | ||
} | ||
visitSync(visitors, node) { | ||
for (let [plugin, visitor] of visitors) { | ||
this.result.lastPlugin = plugin | ||
let promise | ||
try { | ||
promise = visitor(node, this.helpers) | ||
} catch (e) { | ||
throw this.handleError(e, node.proxyOf) | ||
} | ||
if (node.type !== 'root' && node.type !== 'document' && !node.parent) { | ||
return true | ||
} | ||
if (isPromise(promise)) { | ||
throw this.getAsyncError() | ||
} | ||
} | ||
} | ||
visitTick(stack) { | ||
@@ -547,2 +492,57 @@ let visit = stack[stack.length - 1] | ||
} | ||
walkSync(node) { | ||
node[isClean] = true | ||
let events = getEvents(node) | ||
for (let event of events) { | ||
if (event === CHILDREN) { | ||
if (node.nodes) { | ||
node.each(child => { | ||
if (!child[isClean]) this.walkSync(child) | ||
}) | ||
} | ||
} else { | ||
let visitors = this.listeners[event] | ||
if (visitors) { | ||
if (this.visitSync(visitors, node.toProxy())) return | ||
} | ||
} | ||
} | ||
} | ||
warnings() { | ||
return this.sync().warnings() | ||
} | ||
get content() { | ||
return this.stringify().content | ||
} | ||
get css() { | ||
return this.stringify().css | ||
} | ||
get map() { | ||
return this.stringify().map | ||
} | ||
get messages() { | ||
return this.sync().messages | ||
} | ||
get opts() { | ||
return this.result.opts | ||
} | ||
get processor() { | ||
return this.result.processor | ||
} | ||
get root() { | ||
return this.sync().root | ||
} | ||
get [Symbol.toStringTag]() { | ||
return 'LazyResult' | ||
} | ||
} | ||
@@ -549,0 +549,0 @@ |
@@ -1,51 +0,57 @@ | ||
export type List = { | ||
/** | ||
* Safely splits values. | ||
* | ||
* ```js | ||
* Once (root, { list }) { | ||
* list.split('1px calc(10% + 1px)', [' ', '\n', '\t']) //=> ['1px', 'calc(10% + 1px)'] | ||
* } | ||
* ``` | ||
* | ||
* @param string separated values. | ||
* @param separators array of separators. | ||
* @param last boolean indicator. | ||
* @return Split values. | ||
*/ | ||
split(string: string, separators: string[], last: boolean): string[] | ||
/** | ||
* Safely splits space-separated values (such as those for `background`, | ||
* `border-radius`, and other shorthand properties). | ||
* | ||
* ```js | ||
* Once (root, { list }) { | ||
* list.space('1px calc(10% + 1px)') //=> ['1px', 'calc(10% + 1px)'] | ||
* } | ||
* ``` | ||
* | ||
* @param str Space-separated values. | ||
* @return Split values. | ||
*/ | ||
space(str: string): string[] | ||
declare namespace list { | ||
type List = { | ||
/** | ||
* Safely splits comma-separated values (such as those for `transition-*` | ||
* and `background` properties). | ||
* | ||
* ```js | ||
* Once (root, { list }) { | ||
* list.comma('black, linear-gradient(white, black)') | ||
* //=> ['black', 'linear-gradient(white, black)'] | ||
* } | ||
* ``` | ||
* | ||
* @param str Comma-separated values. | ||
* @return Split values. | ||
*/ | ||
comma(str: string): string[] | ||
/** | ||
* Safely splits comma-separated values (such as those for `transition-*` | ||
* and `background` properties). | ||
* | ||
* ```js | ||
* Once (root, { list }) { | ||
* list.comma('black, linear-gradient(white, black)') | ||
* //=> ['black', 'linear-gradient(white, black)'] | ||
* } | ||
* ``` | ||
* | ||
* @param str Comma-separated values. | ||
* @return Split values. | ||
*/ | ||
comma(str: string): string[] | ||
default: List | ||
/** | ||
* Safely splits space-separated values (such as those for `background`, | ||
* `border-radius`, and other shorthand properties). | ||
* | ||
* ```js | ||
* Once (root, { list }) { | ||
* list.space('1px calc(10% + 1px)') //=> ['1px', 'calc(10% + 1px)'] | ||
* } | ||
* ``` | ||
* | ||
* @param str Space-separated values. | ||
* @return Split values. | ||
*/ | ||
space(str: string): string[] | ||
/** | ||
* Safely splits values. | ||
* | ||
* ```js | ||
* Once (root, { list }) { | ||
* list.split('1px calc(10% + 1px)', [' ', '\n', '\t']) //=> ['1px', 'calc(10% + 1px)'] | ||
* } | ||
* ``` | ||
* | ||
* @param string separated values. | ||
* @param separators array of separators. | ||
* @param last boolean indicator. | ||
* @return Split values. | ||
*/ | ||
split(string: string, separators: string[], last: boolean): string[] | ||
} | ||
} | ||
declare const list: List | ||
// eslint-disable-next-line @typescript-eslint/no-redeclare | ||
declare const list: list.List | ||
export default list | ||
export = list |
'use strict' | ||
let list = { | ||
comma(string) { | ||
return list.split(string, [','], true) | ||
}, | ||
space(string) { | ||
let spaces = [' ', '\n', '\t'] | ||
return list.split(string, spaces) | ||
}, | ||
split(string, separators, last) { | ||
@@ -45,11 +54,2 @@ let array = [] | ||
return array | ||
}, | ||
space(string) { | ||
let spaces = [' ', '\n', '\t'] | ||
return list.split(string, spaces) | ||
}, | ||
comma(string) { | ||
return list.split(string, [','], true) | ||
} | ||
@@ -56,0 +56,0 @@ } |
'use strict' | ||
let { SourceMapConsumer, SourceMapGenerator } = require('source-map-js') | ||
let { dirname, resolve, relative, sep } = require('path') | ||
let { dirname, relative, resolve, sep } = require('path') | ||
let { pathToFileURL } = require('url') | ||
@@ -19,98 +19,28 @@ | ||
this.css = cssString | ||
} | ||
this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute | ||
isMap() { | ||
if (typeof this.opts.map !== 'undefined') { | ||
return !!this.opts.map | ||
} | ||
return this.previous().length > 0 | ||
this.memoizedFileURLs = new Map() | ||
this.memoizedPaths = new Map() | ||
this.memoizedURLs = new Map() | ||
} | ||
previous() { | ||
if (!this.previousMaps) { | ||
this.previousMaps = [] | ||
if (this.root) { | ||
this.root.walk(node => { | ||
if (node.source && node.source.input.map) { | ||
let map = node.source.input.map | ||
if (!this.previousMaps.includes(map)) { | ||
this.previousMaps.push(map) | ||
} | ||
} | ||
}) | ||
} else { | ||
let input = new Input(this.css, this.opts) | ||
if (input.map) this.previousMaps.push(input.map) | ||
} | ||
} | ||
addAnnotation() { | ||
let content | ||
return this.previousMaps | ||
} | ||
isInline() { | ||
if (typeof this.mapOpts.inline !== 'undefined') { | ||
return this.mapOpts.inline | ||
if (this.isInline()) { | ||
content = | ||
'data:application/json;base64,' + this.toBase64(this.map.toString()) | ||
} else if (typeof this.mapOpts.annotation === 'string') { | ||
content = this.mapOpts.annotation | ||
} else if (typeof this.mapOpts.annotation === 'function') { | ||
content = this.mapOpts.annotation(this.opts.to, this.root) | ||
} else { | ||
content = this.outputFile() + '.map' | ||
} | ||
let eol = '\n' | ||
if (this.css.includes('\r\n')) eol = '\r\n' | ||
let annotation = this.mapOpts.annotation | ||
if (typeof annotation !== 'undefined' && annotation !== true) { | ||
return false | ||
} | ||
if (this.previous().length) { | ||
return this.previous().some(i => i.inline) | ||
} | ||
return true | ||
this.css += eol + '/*# sourceMappingURL=' + content + ' */' | ||
} | ||
isSourcesContent() { | ||
if (typeof this.mapOpts.sourcesContent !== 'undefined') { | ||
return this.mapOpts.sourcesContent | ||
} | ||
if (this.previous().length) { | ||
return this.previous().some(i => i.withContent()) | ||
} | ||
return true | ||
} | ||
clearAnnotation() { | ||
if (this.mapOpts.annotation === false) return | ||
if (this.root) { | ||
let node | ||
for (let i = this.root.nodes.length - 1; i >= 0; i--) { | ||
node = this.root.nodes[i] | ||
if (node.type !== 'comment') continue | ||
if (node.text.indexOf('# sourceMappingURL=') === 0) { | ||
this.root.removeChild(i) | ||
} | ||
} | ||
} else if (this.css) { | ||
this.css = this.css.replace(/(\n)?\/\*#[\S\s]*?\*\/$/gm, '') | ||
} | ||
} | ||
setSourcesContent() { | ||
let already = {} | ||
if (this.root) { | ||
this.root.walk(node => { | ||
if (node.source) { | ||
let from = node.source.input.from | ||
if (from && !already[from]) { | ||
already[from] = true | ||
this.map.setSourceContent( | ||
this.toUrl(this.path(from)), | ||
node.source.input.css | ||
) | ||
} | ||
} | ||
}) | ||
} else if (this.css) { | ||
let from = this.opts.from | ||
? this.toUrl(this.path(this.opts.from)) | ||
: '<no source>' | ||
this.map.setSourceContent(from, this.css) | ||
} | ||
} | ||
applyPrevMaps() { | ||
@@ -135,52 +65,32 @@ for (let prev of this.previous()) { | ||
isAnnotation() { | ||
if (this.isInline()) { | ||
return true | ||
} | ||
if (typeof this.mapOpts.annotation !== 'undefined') { | ||
return this.mapOpts.annotation | ||
} | ||
if (this.previous().length) { | ||
return this.previous().some(i => i.annotation) | ||
} | ||
return true | ||
} | ||
clearAnnotation() { | ||
if (this.mapOpts.annotation === false) return | ||
toBase64(str) { | ||
if (Buffer) { | ||
return Buffer.from(str).toString('base64') | ||
} else { | ||
return window.btoa(unescape(encodeURIComponent(str))) | ||
if (this.root) { | ||
let node | ||
for (let i = this.root.nodes.length - 1; i >= 0; i--) { | ||
node = this.root.nodes[i] | ||
if (node.type !== 'comment') continue | ||
if (node.text.indexOf('# sourceMappingURL=') === 0) { | ||
this.root.removeChild(i) | ||
} | ||
} | ||
} else if (this.css) { | ||
this.css = this.css.replace(/(\n)?\/\*#[\S\s]*?\*\/$/gm, '') | ||
} | ||
} | ||
addAnnotation() { | ||
let content | ||
if (this.isInline()) { | ||
content = | ||
'data:application/json;base64,' + this.toBase64(this.map.toString()) | ||
} else if (typeof this.mapOpts.annotation === 'string') { | ||
content = this.mapOpts.annotation | ||
} else if (typeof this.mapOpts.annotation === 'function') { | ||
content = this.mapOpts.annotation(this.opts.to, this.root) | ||
generate() { | ||
this.clearAnnotation() | ||
if (pathAvailable && sourceMapAvailable && this.isMap()) { | ||
return this.generateMap() | ||
} else { | ||
content = this.outputFile() + '.map' | ||
let result = '' | ||
this.stringify(this.root, i => { | ||
result += i | ||
}) | ||
return [result] | ||
} | ||
let eol = '\n' | ||
if (this.css.includes('\r\n')) eol = '\r\n' | ||
this.css += eol + '/*# sourceMappingURL=' + content + ' */' | ||
} | ||
outputFile() { | ||
if (this.opts.to) { | ||
return this.path(this.opts.to) | ||
} else if (this.opts.from) { | ||
return this.path(this.opts.from) | ||
} else { | ||
return 'to.css' | ||
} | ||
} | ||
generateMap() { | ||
@@ -196,7 +106,7 @@ if (this.root) { | ||
this.map.addMapping({ | ||
generated: { column: 0, line: 1 }, | ||
original: { column: 0, line: 1 }, | ||
source: this.opts.from | ||
? this.toUrl(this.path(this.opts.from)) | ||
: '<no source>', | ||
generated: { line: 1, column: 0 }, | ||
original: { line: 1, column: 0 } | ||
: '<no source>' | ||
}) | ||
@@ -216,40 +126,2 @@ } | ||
path(file) { | ||
if (file.indexOf('<') === 0) return file | ||
if (/^\w+:\/\//.test(file)) return file | ||
if (this.mapOpts.absolute) return file | ||
let from = this.opts.to ? dirname(this.opts.to) : '.' | ||
if (typeof this.mapOpts.annotation === 'string') { | ||
from = dirname(resolve(from, this.mapOpts.annotation)) | ||
} | ||
file = relative(from, file) | ||
return file | ||
} | ||
toUrl(path) { | ||
if (sep === '\\') { | ||
path = path.replace(/\\/g, '/') | ||
} | ||
return encodeURI(path).replace(/[#?]/g, encodeURIComponent) | ||
} | ||
sourcePath(node) { | ||
if (this.mapOpts.from) { | ||
return this.toUrl(this.mapOpts.from) | ||
} else if (this.mapOpts.absolute) { | ||
if (pathToFileURL) { | ||
return pathToFileURL(node.source.input.from).toString() | ||
} else { | ||
throw new Error( | ||
'`map.absolute` option is not available in this PostCSS build' | ||
) | ||
} | ||
} else { | ||
return this.toUrl(this.path(node.source.input.from)) | ||
} | ||
} | ||
generateString() { | ||
@@ -264,5 +136,5 @@ this.css = '' | ||
let mapping = { | ||
source: '', | ||
generated: { line: 0, column: 0 }, | ||
original: { line: 0, column: 0 } | ||
generated: { column: 0, line: 0 }, | ||
original: { column: 0, line: 0 }, | ||
source: '' | ||
} | ||
@@ -301,3 +173,5 @@ | ||
let p = node.parent || { raws: {} } | ||
if (node.type !== 'decl' || node !== p.last || p.raws.semicolon) { | ||
let childless = | ||
node.type === 'decl' || (node.type === 'atrule' && !node.nodes) | ||
if (!childless || node !== p.last || p.raws.semicolon) { | ||
if (node.source && node.source.end) { | ||
@@ -323,16 +197,170 @@ mapping.source = this.sourcePath(node) | ||
generate() { | ||
this.clearAnnotation() | ||
if (pathAvailable && sourceMapAvailable && this.isMap()) { | ||
return this.generateMap() | ||
isAnnotation() { | ||
if (this.isInline()) { | ||
return true | ||
} | ||
if (typeof this.mapOpts.annotation !== 'undefined') { | ||
return this.mapOpts.annotation | ||
} | ||
if (this.previous().length) { | ||
return this.previous().some(i => i.annotation) | ||
} | ||
return true | ||
} | ||
isInline() { | ||
if (typeof this.mapOpts.inline !== 'undefined') { | ||
return this.mapOpts.inline | ||
} | ||
let annotation = this.mapOpts.annotation | ||
if (typeof annotation !== 'undefined' && annotation !== true) { | ||
return false | ||
} | ||
if (this.previous().length) { | ||
return this.previous().some(i => i.inline) | ||
} | ||
return true | ||
} | ||
isMap() { | ||
if (typeof this.opts.map !== 'undefined') { | ||
return !!this.opts.map | ||
} | ||
return this.previous().length > 0 | ||
} | ||
isSourcesContent() { | ||
if (typeof this.mapOpts.sourcesContent !== 'undefined') { | ||
return this.mapOpts.sourcesContent | ||
} | ||
if (this.previous().length) { | ||
return this.previous().some(i => i.withContent()) | ||
} | ||
return true | ||
} | ||
outputFile() { | ||
if (this.opts.to) { | ||
return this.path(this.opts.to) | ||
} else if (this.opts.from) { | ||
return this.path(this.opts.from) | ||
} else { | ||
let result = '' | ||
this.stringify(this.root, i => { | ||
result += i | ||
return 'to.css' | ||
} | ||
} | ||
path(file) { | ||
if (this.mapOpts.absolute) return file | ||
if (file.charCodeAt(0) === 60 /* `<` */) return file | ||
if (/^\w+:\/\//.test(file)) return file | ||
let cached = this.memoizedPaths.get(file) | ||
if (cached) return cached | ||
let from = this.opts.to ? dirname(this.opts.to) : '.' | ||
if (typeof this.mapOpts.annotation === 'string') { | ||
from = dirname(resolve(from, this.mapOpts.annotation)) | ||
} | ||
let path = relative(from, file) | ||
this.memoizedPaths.set(file, path) | ||
return path | ||
} | ||
previous() { | ||
if (!this.previousMaps) { | ||
this.previousMaps = [] | ||
if (this.root) { | ||
this.root.walk(node => { | ||
if (node.source && node.source.input.map) { | ||
let map = node.source.input.map | ||
if (!this.previousMaps.includes(map)) { | ||
this.previousMaps.push(map) | ||
} | ||
} | ||
}) | ||
} else { | ||
let input = new Input(this.css, this.opts) | ||
if (input.map) this.previousMaps.push(input.map) | ||
} | ||
} | ||
return this.previousMaps | ||
} | ||
setSourcesContent() { | ||
let already = {} | ||
if (this.root) { | ||
this.root.walk(node => { | ||
if (node.source) { | ||
let from = node.source.input.from | ||
if (from && !already[from]) { | ||
already[from] = true | ||
let fromUrl = this.usesFileUrls | ||
? this.toFileUrl(from) | ||
: this.toUrl(this.path(from)) | ||
this.map.setSourceContent(fromUrl, node.source.input.css) | ||
} | ||
} | ||
}) | ||
return [result] | ||
} else if (this.css) { | ||
let from = this.opts.from | ||
? this.toUrl(this.path(this.opts.from)) | ||
: '<no source>' | ||
this.map.setSourceContent(from, this.css) | ||
} | ||
} | ||
sourcePath(node) { | ||
if (this.mapOpts.from) { | ||
return this.toUrl(this.mapOpts.from) | ||
} else if (this.usesFileUrls) { | ||
return this.toFileUrl(node.source.input.from) | ||
} else { | ||
return this.toUrl(this.path(node.source.input.from)) | ||
} | ||
} | ||
toBase64(str) { | ||
if (Buffer) { | ||
return Buffer.from(str).toString('base64') | ||
} else { | ||
return window.btoa(unescape(encodeURIComponent(str))) | ||
} | ||
} | ||
toFileUrl(path) { | ||
let cached = this.memoizedFileURLs.get(path) | ||
if (cached) return cached | ||
if (pathToFileURL) { | ||
let fileURL = pathToFileURL(path).toString() | ||
this.memoizedFileURLs.set(path, fileURL) | ||
return fileURL | ||
} else { | ||
throw new Error( | ||
'`map.absolute` option is not available in this PostCSS build' | ||
) | ||
} | ||
} | ||
toUrl(path) { | ||
let cached = this.memoizedURLs.get(path) | ||
if (cached) return cached | ||
if (sep === '\\') { | ||
path = path.replace(/\\/g, '/') | ||
} | ||
let url = encodeURI(path).replace(/[#?]/g, encodeURIComponent) | ||
this.memoizedURLs.set(path, url) | ||
return url | ||
} | ||
} | ||
module.exports = MapGenerator |
@@ -1,8 +0,13 @@ | ||
import Result, { Message, ResultOptions } from './result.js' | ||
import LazyResult from './lazy-result.js' | ||
import { SourceMap } from './postcss.js' | ||
import Processor from './processor.js' | ||
import Result, { Message, ResultOptions } from './result.js' | ||
import Root from './root.js' | ||
import Warning from './warning.js' | ||
import Root from './root.js' | ||
import LazyResult from './lazy-result.js' | ||
declare namespace NoWorkResult { | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { NoWorkResult_ as default } | ||
} | ||
/** | ||
@@ -20,19 +25,23 @@ * A Promise proxy for the result of PostCSS transformations. | ||
*/ | ||
export default class NoWorkResult implements LazyResult { | ||
then: Promise<Result>['then'] | ||
catch: Promise<Result>['catch'] | ||
finally: Promise<Result>['finally'] | ||
declare class NoWorkResult_ implements LazyResult<Root> { | ||
catch: Promise<Result<Root>>['catch'] | ||
finally: Promise<Result<Root>>['finally'] | ||
then: Promise<Result<Root>>['then'] | ||
constructor(processor: Processor, css: string, opts: ResultOptions) | ||
get [Symbol.toStringTag](): string | ||
get processor(): Processor | ||
get opts(): ResultOptions | ||
async(): Promise<Result<Root>> | ||
sync(): Result<Root> | ||
toString(): string | ||
warnings(): Warning[] | ||
get content(): string | ||
get css(): string | ||
get content(): string | ||
get map(): SourceMap | ||
get messages(): Message[] | ||
get opts(): ResultOptions | ||
get processor(): Processor | ||
get root(): Root | ||
get messages(): Message[] | ||
warnings(): Warning[] | ||
toString(): string | ||
sync(): Result | ||
async(): Promise<Result> | ||
get [Symbol.toStringTag](): string | ||
} | ||
declare class NoWorkResult extends NoWorkResult_ {} | ||
export = NoWorkResult |
@@ -43,18 +43,42 @@ 'use strict' | ||
get [Symbol.toStringTag]() { | ||
return 'NoWorkResult' | ||
async() { | ||
if (this.error) return Promise.reject(this.error) | ||
return Promise.resolve(this.result) | ||
} | ||
get processor() { | ||
return this.result.processor | ||
catch(onRejected) { | ||
return this.async().catch(onRejected) | ||
} | ||
get opts() { | ||
return this.result.opts | ||
finally(onFinally) { | ||
return this.async().then(onFinally, onFinally) | ||
} | ||
get css() { | ||
return this.result.css | ||
sync() { | ||
if (this.error) throw this.error | ||
return this.result | ||
} | ||
then(onFulfilled, onRejected) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (!('from' in this._opts)) { | ||
warnOnce( | ||
'Without `from` option PostCSS could generate wrong source map ' + | ||
'and will not find Browserslist config. Set it to CSS file path ' + | ||
'or to `undefined` to prevent this warning.' | ||
) | ||
} | ||
} | ||
return this.async().then(onFulfilled, onRejected) | ||
} | ||
toString() { | ||
return this._css | ||
} | ||
warnings() { | ||
return [] | ||
} | ||
get content() { | ||
@@ -64,2 +88,6 @@ return this.result.css | ||
get css() { | ||
return this.result.css | ||
} | ||
get map() { | ||
@@ -69,2 +97,14 @@ return this.result.map | ||
get messages() { | ||
return [] | ||
} | ||
get opts() { | ||
return this.result.opts | ||
} | ||
get processor() { | ||
return this.result.processor | ||
} | ||
get root() { | ||
@@ -92,45 +132,5 @@ if (this._root) { | ||
get messages() { | ||
return [] | ||
get [Symbol.toStringTag]() { | ||
return 'NoWorkResult' | ||
} | ||
warnings() { | ||
return [] | ||
} | ||
toString() { | ||
return this._css | ||
} | ||
then(onFulfilled, onRejected) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (!('from' in this._opts)) { | ||
warnOnce( | ||
'Without `from` option PostCSS could generate wrong source map ' + | ||
'and will not find Browserslist config. Set it to CSS file path ' + | ||
'or to `undefined` to prevent this warning.' | ||
) | ||
} | ||
} | ||
return this.async().then(onFulfilled, onRejected) | ||
} | ||
catch(onRejected) { | ||
return this.async().catch(onRejected) | ||
} | ||
finally(onFinally) { | ||
return this.async().then(onFinally, onFinally) | ||
} | ||
async() { | ||
if (this.error) return Promise.reject(this.error) | ||
return Promise.resolve(this.result) | ||
} | ||
sync() { | ||
if (this.error) throw this.error | ||
return this.result | ||
} | ||
} | ||
@@ -137,0 +137,0 @@ |
@@ -0,134 +1,187 @@ | ||
import AtRule = require('./at-rule.js') | ||
import { AtRuleProps } from './at-rule.js' | ||
import Comment, { CommentProps } from './comment.js' | ||
import Container from './container.js' | ||
import CssSyntaxError from './css-syntax-error.js' | ||
import Declaration, { DeclarationProps } from './declaration.js' | ||
import Comment, { CommentProps } from './comment.js' | ||
import Document from './document.js' | ||
import Input from './input.js' | ||
import { Stringifier, Syntax } from './postcss.js' | ||
import AtRule, { AtRuleProps } from './at-rule.js' | ||
import Result from './result.js' | ||
import Root from './root.js' | ||
import Rule, { RuleProps } from './rule.js' | ||
import Warning, { WarningOptions } from './warning.js' | ||
import CssSyntaxError from './css-syntax-error.js' | ||
import Result from './result.js' | ||
import Input from './input.js' | ||
import Root from './root.js' | ||
import Document from './document.js' | ||
import Container from './container.js' | ||
export type ChildNode = AtRule | Rule | Declaration | Comment | ||
declare namespace Node { | ||
export type ChildNode = AtRule.default | Comment | Declaration | Rule | ||
export type AnyNode = AtRule | Rule | Declaration | Comment | Root | Document | ||
export type AnyNode = | ||
| AtRule.default | ||
| Comment | ||
| Declaration | ||
| Document | ||
| Root | ||
| Rule | ||
export type ChildProps = | ||
| AtRuleProps | ||
| RuleProps | ||
| DeclarationProps | ||
| CommentProps | ||
export type ChildProps = | ||
| AtRuleProps | ||
| CommentProps | ||
| DeclarationProps | ||
| RuleProps | ||
export interface Position { | ||
/** | ||
* Source offset in file. It starts from 0. | ||
*/ | ||
offset: number | ||
export interface Position { | ||
/** | ||
* Source line in file. In contrast to `offset` it starts from 1. | ||
*/ | ||
column: number | ||
/** | ||
* Source line in file. In contrast to `offset` it starts from 1. | ||
*/ | ||
column: number | ||
/** | ||
* Source column in file. | ||
*/ | ||
line: number | ||
/** | ||
* Source column in file. | ||
*/ | ||
line: number | ||
} | ||
/** | ||
* Source offset in file. It starts from 0. | ||
*/ | ||
offset: number | ||
} | ||
export interface Range { | ||
/** | ||
* Start position, inclusive. | ||
*/ | ||
start: Position | ||
export interface Range { | ||
/** | ||
* End position, exclusive. | ||
*/ | ||
end: Position | ||
/** | ||
* Start position, inclusive. | ||
*/ | ||
start: Position | ||
} | ||
/** | ||
* End position, exclusive. | ||
* Source represents an interface for the {@link Node.source} property. | ||
*/ | ||
end: Position | ||
} | ||
export interface Source { | ||
/** | ||
* The inclusive ending position for the source | ||
* code of a node. | ||
*/ | ||
end?: Position | ||
export interface Source { | ||
/** | ||
* The source file from where a node has originated. | ||
*/ | ||
input: Input | ||
/** | ||
* The inclusive starting position for the source | ||
* code of a node. | ||
*/ | ||
start?: Position | ||
} | ||
/** | ||
* The file source of the node. | ||
* Interface represents an interface for an object received | ||
* as parameter by Node class constructor. | ||
*/ | ||
input: Input | ||
/** | ||
* The inclusive starting position of the node’s source. | ||
*/ | ||
start?: Position | ||
/** | ||
* The inclusive ending position of the node's source. | ||
*/ | ||
end?: Position | ||
} | ||
export interface NodeProps { | ||
source?: Source | ||
} | ||
export interface NodeProps { | ||
source?: Source | ||
} | ||
export interface NodeErrorOptions { | ||
/** | ||
* An ending index inside a node's string that should be highlighted as | ||
* source of error. | ||
*/ | ||
endIndex?: number | ||
/** | ||
* An index inside a node's string that should be highlighted as source | ||
* of error. | ||
*/ | ||
index?: number | ||
/** | ||
* Plugin name that created this error. PostCSS will set it automatically. | ||
*/ | ||
plugin?: string | ||
/** | ||
* A word inside a node's string, that should be highlighted as source | ||
* of error. | ||
*/ | ||
word?: string | ||
} | ||
interface NodeErrorOptions { | ||
/** | ||
* Plugin name that created this error. PostCSS will set it automatically. | ||
*/ | ||
plugin?: string | ||
/** | ||
* A word inside a node's string, that should be highlighted as source | ||
* of error. | ||
*/ | ||
word?: string | ||
/** | ||
* An index inside a node's string that should be highlighted as source | ||
* of error. | ||
*/ | ||
index?: number | ||
/** | ||
* An ending index inside a node's string that should be highlighted as | ||
* source of error. | ||
*/ | ||
endIndex?: number | ||
// eslint-disable-next-line @typescript-eslint/no-shadow | ||
class Node extends Node_ {} | ||
export { Node as default } | ||
} | ||
/** | ||
* All node classes inherit the following common methods. | ||
* It represents an abstract class that handles common | ||
* methods for other CSS abstract syntax tree nodes. | ||
* | ||
* You should not extend this classes to create AST for selector or value | ||
* parser. | ||
* Any node that represents CSS selector or value should | ||
* not extend the `Node` class. | ||
*/ | ||
export default abstract class Node { | ||
declare abstract class Node_ { | ||
/** | ||
* tring representing the node’s type. Possible values are `root`, `atrule`, | ||
* `rule`, `decl`, or `comment`. | ||
* It represents parent of the current node. | ||
* | ||
* ```js | ||
* new Declaration({ prop: 'color', value: 'black' }).type //=> 'decl' | ||
* root.nodes[0].parent === root //=> true | ||
* ``` | ||
*/ | ||
type: string | ||
parent: Container | Document | undefined | ||
/** | ||
* The node’s parent node. | ||
* It represents unnecessary whitespace and characters present | ||
* in the css source code. | ||
* | ||
* Information to generate byte-to-byte equal node string as it was | ||
* in the origin input. | ||
* | ||
* The properties of the raws object are decided by parser, | ||
* the default parser uses the following properties: | ||
* | ||
* * `before`: the space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
* * `after`: the space symbols after the last child of the node | ||
* to the end of the node. | ||
* * `between`: the symbols between the property and value | ||
* for declarations, selector and `{` for rules, or last parameter | ||
* and `{` for at-rules. | ||
* * `semicolon`: contains true if the last child has | ||
* an (optional) semicolon. | ||
* * `afterName`: the space between the at-rule name and its parameters. | ||
* * `left`: the space symbols between `/*` and the comment’s text. | ||
* * `right`: the space symbols between the comment’s text | ||
* and <code>*/</code>. | ||
* - `important`: the content of the important statement, | ||
* if it is not just `!important`. | ||
* | ||
* PostCSS filters out the comments inside selectors, declaration values | ||
* and at-rule parameters but it stores the origin content in raws. | ||
* | ||
* ```js | ||
* root.nodes[0].parent === root | ||
* const root = postcss.parse('a {\n color:black\n}') | ||
* root.first.first.raws //=> { before: '\n ', between: ':' } | ||
* ``` | ||
*/ | ||
parent: Document | Container | undefined | ||
raws: any | ||
/** | ||
* The input source of the node. | ||
* It represents information related to origin of a node and is required | ||
* for generating source maps. | ||
* | ||
* The property is used in source map generation. | ||
* The nodes that are created manually using the public APIs | ||
* provided by PostCSS will have `source` undefined and | ||
* will be absent in the source map. | ||
* | ||
* If you create a node manually (e.g., with `postcss.decl()`), | ||
* that node will not have a `source` property and will be absent | ||
* from the source map. For this reason, the plugin developer should | ||
* consider cloning nodes to create new ones (in which case the new node’s | ||
* source will reference the original, cloned node) or setting | ||
* the `source` property manually. | ||
* For this reason, the plugin developer should consider | ||
* duplicating nodes as the duplicate node will have the | ||
* same source as the original node by default or assign | ||
* source to a node created manually. | ||
* | ||
* ```js | ||
* decl.source.input.from //=> '/home/ai/a.sass' | ||
* decl.source.input.from //=> '/home/ai/source.css' | ||
* decl.source.start //=> { line: 10, column: 2 } | ||
@@ -139,3 +192,3 @@ * decl.source.end //=> { line: 10, column: 12 } | ||
* ```js | ||
* // Bad | ||
* // Incorrect method, source not specified! | ||
* const prefixed = postcss.decl({ | ||
@@ -146,4 +199,6 @@ * prop: '-moz-' + decl.prop, | ||
* | ||
* // Good | ||
* const prefixed = decl.clone({ prop: '-moz-' + decl.prop }) | ||
* // Correct method, source is inherited when duplicating. | ||
* const prefixed = decl.clone({ | ||
* prop: '-moz-' + decl.prop | ||
* }) | ||
* ``` | ||
@@ -153,141 +208,91 @@ * | ||
* if (atrule.name === 'add-link') { | ||
* const rule = postcss.rule({ selector: 'a', source: atrule.source }) | ||
* atrule.parent.insertBefore(atrule, rule) | ||
* const rule = postcss.rule({ | ||
* selector: 'a', | ||
* source: atrule.source | ||
* }) | ||
* | ||
* atrule.parent.insertBefore(atrule, rule) | ||
* } | ||
* ``` | ||
*/ | ||
source?: Source | ||
source?: Node.Source | ||
/** | ||
* Information to generate byte-to-byte equal node string as it was | ||
* in the origin input. | ||
* It represents type of a node in | ||
* an abstract syntax tree. | ||
* | ||
* Every parser saves its own properties, | ||
* but the default CSS parser uses: | ||
* A type of node helps in identification of a node | ||
* and perform operation based on it's type. | ||
* | ||
* * `before`: the space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
* * `after`: the space symbols after the last child of the node | ||
* to the end of the node. | ||
* * `between`: the symbols between the property and value | ||
* for declarations, selector and `{` for rules, or last parameter | ||
* and `{` for at-rules. | ||
* * `semicolon`: contains true if the last child has | ||
* an (optional) semicolon. | ||
* * `afterName`: the space between the at-rule name and its parameters. | ||
* * `left`: the space symbols between `/*` and the comment’s text. | ||
* * `right`: the space symbols between the comment’s text | ||
* and <code>*/</code>. | ||
* * `important`: the content of the important statement, | ||
* if it is not just `!important`. | ||
* ```js | ||
* const declaration = new Declaration({ | ||
* prop: 'color', | ||
* value: 'black' | ||
* }) | ||
* | ||
* PostCSS cleans selectors, declaration values and at-rule parameters | ||
* from comments and extra spaces, but it stores origin content in raws | ||
* properties. As such, if you don’t change a declaration’s value, | ||
* PostCSS will use the raw value with comments. | ||
* | ||
* ```js | ||
* const root = postcss.parse('a {\n color:black\n}') | ||
* root.first.first.raws //=> { before: '\n ', between: ':' } | ||
* declaration.type //=> 'decl' | ||
* ``` | ||
*/ | ||
raws: any | ||
type: string | ||
/** | ||
* @param defaults Value for node properties. | ||
*/ | ||
constructor(defaults?: object) | ||
/** | ||
* Returns a `CssSyntaxError` instance containing the original position | ||
* of the node in the source, showing line and column numbers and also | ||
* a small excerpt to facilitate debugging. | ||
* Insert new node after current node to current node’s parent. | ||
* | ||
* If present, an input source map will be used to get the original position | ||
* of the source, even from a previous compilation step | ||
* (e.g., from Sass compilation). | ||
* Just alias for `node.parent.insertAfter(node, add)`. | ||
* | ||
* This method produces very useful error messages. | ||
* | ||
* ```js | ||
* if (!variables[name]) { | ||
* throw decl.error(`Unknown variable ${name}`, { word: name }) | ||
* // CssSyntaxError: postcss-vars:a.sass:4:3: Unknown variable $black | ||
* // color: $black | ||
* // a | ||
* // ^ | ||
* // background: white | ||
* } | ||
* decl.after('color: black') | ||
* ``` | ||
* | ||
* @param message Error description. | ||
* @param opts Options. | ||
* | ||
* @return Error object to throw it. | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
*/ | ||
error(message: string, options?: NodeErrorOptions): CssSyntaxError | ||
after(newNode: Node | Node.ChildProps | Node[] | string): this | ||
/** | ||
* This method is provided as a convenience wrapper for `Result#warn`. | ||
* It assigns properties to an existing node instance. | ||
* | ||
* ```js | ||
* Declaration: { | ||
* bad: (decl, { result }) => { | ||
* decl.warn(result, 'Deprecated property bad') | ||
* } | ||
* } | ||
* decl.assign({ prop: 'word-wrap', value: 'break-word' }) | ||
* ``` | ||
* | ||
* @param result The `Result` instance that will receive the warning. | ||
* @param text Warning message. | ||
* @param opts Warning Options. | ||
* @param overrides New properties to override the node. | ||
* | ||
* @return Created warning object. | ||
* @return `this` for method chaining. | ||
*/ | ||
warn(result: Result, text: string, opts?: WarningOptions): Warning | ||
assign(overrides: object): this | ||
/** | ||
* Removes the node from its parent and cleans the parent properties | ||
* from the node and its children. | ||
* Insert new node before current node to current node’s parent. | ||
* | ||
* ```js | ||
* if (decl.prop.match(/^-webkit-/)) { | ||
* decl.remove() | ||
* } | ||
* ``` | ||
* Just alias for `node.parent.insertBefore(node, add)`. | ||
* | ||
* @return Node to make calls chain. | ||
*/ | ||
remove(): this | ||
/** | ||
* Returns a CSS string representing the node. | ||
* | ||
* ```js | ||
* new Rule({ selector: 'a' }).toString() //=> "a {}" | ||
* decl.before('content: ""') | ||
* ``` | ||
* | ||
* @param stringifier A syntax to use in string generation. | ||
* @return CSS string of this node. | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
*/ | ||
toString(stringifier?: Stringifier | Syntax): string | ||
before(newNode: Node | Node.ChildProps | Node[] | string): this | ||
/** | ||
* Assigns properties to the current node. | ||
* Clear the code style properties for the node and its children. | ||
* | ||
* ```js | ||
* decl.assign({ prop: 'word-wrap', value: 'break-word' }) | ||
* node.raws.before //=> ' ' | ||
* node.cleanRaws() | ||
* node.raws.before //=> undefined | ||
* ``` | ||
* | ||
* @param overrides New properties to override the node. | ||
* @return Current node to methods chain. | ||
* @param keepBetween Keep the `raws.between` symbols. | ||
*/ | ||
assign(overrides: object): this | ||
cleanRaws(keepBetween?: boolean): void | ||
/** | ||
* Returns an exact clone of the node. | ||
* It creates clone of an existing node, which includes all the properties | ||
* and their values, that includes `raws` but not `type`. | ||
* | ||
* The resulting cloned node and its (cloned) children will retain | ||
* code style properties. | ||
* | ||
* ```js | ||
@@ -301,8 +306,18 @@ * decl.raws.before //=> "\n " | ||
* @param overrides New properties to override in the clone. | ||
* @return Clone of the node. | ||
* | ||
* @return Duplicate of the node instance. | ||
*/ | ||
clone(overrides?: object): this | ||
clone(overrides?: object): Node | ||
/** | ||
* Shortcut to clone the node and insert the resulting cloned node | ||
* after the current node. | ||
* | ||
* @param overrides New properties to override in the clone. | ||
* @return New node. | ||
*/ | ||
cloneAfter(overrides?: object): Node | ||
/** | ||
* Shortcut to clone the node and insert the resulting cloned node | ||
* before the current node. | ||
@@ -318,30 +333,39 @@ * | ||
*/ | ||
cloneBefore(overrides?: object): this | ||
cloneBefore(overrides?: object): Node | ||
/** | ||
* Shortcut to clone the node and insert the resulting cloned node | ||
* after the current node. | ||
* It creates an instance of the class `CssSyntaxError` and parameters passed | ||
* to this method are assigned to the error instance. | ||
* | ||
* @param overrides New properties to override in the clone. | ||
* @return New node. | ||
*/ | ||
cloneAfter(overrides?: object): this | ||
/** | ||
* Inserts node(s) before the current node and removes the current node. | ||
* The error instance will have description for the | ||
* error, original position of the node in the | ||
* source, showing line and column number. | ||
* | ||
* If any previous map is present, it would be used | ||
* to get original position of the source. | ||
* | ||
* The Previous Map here is referred to the source map | ||
* generated by previous compilation, example: Less, | ||
* Stylus and Sass. | ||
* | ||
* This method returns the error instance instead of | ||
* throwing it. | ||
* | ||
* ```js | ||
* AtRule: { | ||
* mixin: atrule => { | ||
* atrule.replaceWith(mixinRules[atrule.params]) | ||
* } | ||
* if (!variables[name]) { | ||
* throw decl.error(`Unknown variable ${name}`, { word: name }) | ||
* // CssSyntaxError: postcss-vars:a.sass:4:3: Unknown variable $black | ||
* // color: $black | ||
* // a | ||
* // ^ | ||
* // background: white | ||
* } | ||
* ``` | ||
* | ||
* @param nodes Mode(s) to replace current one. | ||
* @return Current node to methods chain. | ||
* @param message Description for the error instance. | ||
* @param options Options for the error instance. | ||
* | ||
* @return Error instance is returned. | ||
*/ | ||
replaceWith( | ||
...nodes: (ChildNode | ChildProps | ChildNode[] | ChildProps[])[] | ||
): this | ||
error(message: string, options?: Node.NodeErrorOptions): CssSyntaxError | ||
@@ -363,5 +387,21 @@ /** | ||
*/ | ||
next(): ChildNode | undefined | ||
next(): Node.ChildNode | undefined | ||
/** | ||
* Get the position for a word or an index inside the node. | ||
* | ||
* @param opts Options. | ||
* @return Position. | ||
*/ | ||
positionBy(opts?: Pick<WarningOptions, 'index' | 'word'>): Node.Position | ||
/** | ||
* Convert string index to line/column. | ||
* | ||
* @param index The symbol number in the node’s string. | ||
* @return Symbol position in file. | ||
*/ | ||
positionInside(index: number): Node.Position | ||
/** | ||
* Returns the previous child of the node’s parent. | ||
@@ -379,75 +419,81 @@ * Returns `undefined` if the current node is the first child. | ||
*/ | ||
prev(): ChildNode | undefined | ||
prev(): Node.ChildNode | undefined | ||
/** | ||
* Insert new node before current node to current node’s parent. | ||
* Get the range for a word or start and end index inside the node. | ||
* The start index is inclusive; the end index is exclusive. | ||
* | ||
* Just alias for `node.parent.insertBefore(node, add)`. | ||
* | ||
* ```js | ||
* decl.before('content: ""') | ||
* ``` | ||
* | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
* @param opts Options. | ||
* @return Range. | ||
*/ | ||
before(newNode: Node | ChildProps | string | Node[]): this | ||
rangeBy( | ||
opts?: Pick<WarningOptions, 'endIndex' | 'index' | 'word'> | ||
): Node.Range | ||
/** | ||
* Insert new node after current node to current node’s parent. | ||
* Returns a `raws` value. If the node is missing | ||
* the code style property (because the node was manually built or cloned), | ||
* PostCSS will try to autodetect the code style property by looking | ||
* at other nodes in the tree. | ||
* | ||
* Just alias for `node.parent.insertAfter(node, add)`. | ||
* | ||
* ```js | ||
* decl.after('color: black') | ||
* const root = postcss.parse('a { background: white }') | ||
* root.nodes[0].append({ prop: 'color', value: 'black' }) | ||
* root.nodes[0].nodes[1].raws.before //=> undefined | ||
* root.nodes[0].nodes[1].raw('before') //=> ' ' | ||
* ``` | ||
* | ||
* @param newNode New node. | ||
* @return This node for methods chain. | ||
* @param prop Name of code style property. | ||
* @param defaultType Name of default value, it can be missed | ||
* if the value is the same as prop. | ||
* @return {string} Code style value. | ||
*/ | ||
after(newNode: Node | ChildProps | string | Node[]): this | ||
raw(prop: string, defaultType?: string): string | ||
/** | ||
* Finds the Root instance of the node’s tree. | ||
* It removes the node from its parent and deletes its parent property. | ||
* | ||
* ```js | ||
* root.nodes[0].nodes[0].root() === root | ||
* if (decl.prop.match(/^-webkit-/)) { | ||
* decl.remove() | ||
* } | ||
* ``` | ||
* | ||
* @return Root parent. | ||
* @return `this` for method chaining. | ||
*/ | ||
root(): Root | ||
remove(): this | ||
/** | ||
* Returns a `Node#raws` value. If the node is missing | ||
* the code style property (because the node was manually built or cloned), | ||
* PostCSS will try to autodetect the code style property by looking | ||
* at other nodes in the tree. | ||
* Inserts node(s) before the current node and removes the current node. | ||
* | ||
* ```js | ||
* const root = postcss.parse('a { background: white }') | ||
* root.nodes[0].append({ prop: 'color', value: 'black' }) | ||
* root.nodes[0].nodes[1].raws.before //=> undefined | ||
* root.nodes[0].nodes[1].raw('before') //=> ' ' | ||
* AtRule: { | ||
* mixin: atrule => { | ||
* atrule.replaceWith(mixinRules[atrule.params]) | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* @param prop Name of code style property. | ||
* @param defaultType Name of default value, it can be missed | ||
* if the value is the same as prop. | ||
* @return {string} Code style value. | ||
* @param nodes Mode(s) to replace current one. | ||
* @return Current node to methods chain. | ||
*/ | ||
raw(prop: string, defaultType?: string): string | ||
replaceWith( | ||
...nodes: ( | ||
| Node.ChildNode | ||
| Node.ChildNode[] | ||
| Node.ChildProps | ||
| Node.ChildProps[] | ||
)[] | ||
): this | ||
/** | ||
* Clear the code style properties for the node and its children. | ||
* Finds the Root instance of the node’s tree. | ||
* | ||
* ```js | ||
* node.raws.before //=> ' ' | ||
* node.cleanRaws() | ||
* node.raws.before //=> undefined | ||
* root.nodes[0].nodes[0].root() === root | ||
* ``` | ||
* | ||
* @param keepBetween Keep the `raws.between` symbols. | ||
* @return Root parent. | ||
*/ | ||
cleanRaws(keepBetween?: boolean): void | ||
root(): Root | ||
@@ -462,25 +508,37 @@ /** | ||
/** | ||
* Convert string index to line/column. | ||
* It compiles the node to browser readable cascading style sheets string | ||
* depending on it's type. | ||
* | ||
* @param index The symbol number in the node’s string. | ||
* @return Symbol position in file. | ||
*/ | ||
positionInside(index: number): Position | ||
/** | ||
* Get the position for a word or an index inside the node. | ||
* ```js | ||
* new Rule({ selector: 'a' }).toString() //=> "a {}" | ||
* ``` | ||
* | ||
* @param opts Options. | ||
* @return Position. | ||
* @param stringifier A syntax to use in string generation. | ||
* @return CSS string of this node. | ||
*/ | ||
positionBy(opts?: Pick<WarningOptions, 'word' | 'index'>): Position | ||
toString(stringifier?: Stringifier | Syntax): string | ||
/** | ||
* Get the range for a word or start and end index inside the node. | ||
* The start index is inclusive; the end index is exclusive. | ||
* It is a wrapper for {@link Result#warn}, providing convenient | ||
* way of generating warnings. | ||
* | ||
* @param opts Options. | ||
* @return Range. | ||
* ```js | ||
* Declaration: { | ||
* bad: (decl, { result }) => { | ||
* decl.warn(result, 'Deprecated property: bad') | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* @param result The `Result` instance that will receive the warning. | ||
* @param message Description for the warning. | ||
* @param options Options for the warning. | ||
* | ||
* @return `Warning` instance is returned | ||
*/ | ||
rangeBy(opts?: Pick<WarningOptions, 'word' | 'index' | 'endIndex'>): Range | ||
warn(result: Result, message: string, options?: WarningOptions): Warning | ||
} | ||
declare class Node extends Node_ { } | ||
export = Node |
392
lib/node.js
@@ -57,38 +57,19 @@ 'use strict' | ||
error(message, opts = {}) { | ||
if (this.source) { | ||
let { start, end } = this.rangeBy(opts) | ||
return this.source.input.error( | ||
message, | ||
{ line: start.line, column: start.column }, | ||
{ line: end.line, column: end.column }, | ||
opts | ||
addToError(error) { | ||
error.postcssNode = this | ||
if (error.stack && this.source && /\n\s{4}at /.test(error.stack)) { | ||
let s = this.source | ||
error.stack = error.stack.replace( | ||
/\n\s{4}at /, | ||
`$&${s.input.from}:${s.start.line}:${s.start.column}$&` | ||
) | ||
} | ||
return new CssSyntaxError(message) | ||
return error | ||
} | ||
warn(result, text, opts) { | ||
let data = { node: this } | ||
for (let i in opts) data[i] = opts[i] | ||
return result.warn(text, data) | ||
} | ||
remove() { | ||
if (this.parent) { | ||
this.parent.removeChild(this) | ||
} | ||
this.parent = undefined | ||
after(add) { | ||
this.parent.insertAfter(this, add) | ||
return this | ||
} | ||
toString(stringifier = stringify) { | ||
if (stringifier.stringify) stringifier = stringifier.stringify | ||
let result = '' | ||
stringifier(this, i => { | ||
result += i | ||
}) | ||
return result | ||
} | ||
assign(overrides = {}) { | ||
@@ -101,2 +82,13 @@ for (let name in overrides) { | ||
before(add) { | ||
this.parent.insertBefore(this, add) | ||
return this | ||
} | ||
cleanRaws(keepBetween) { | ||
delete this.raws.before | ||
delete this.raws.after | ||
if (!keepBetween) delete this.raws.between | ||
} | ||
clone(overrides = {}) { | ||
@@ -110,35 +102,66 @@ let cloned = cloneNode(this) | ||
cloneBefore(overrides = {}) { | ||
cloneAfter(overrides = {}) { | ||
let cloned = this.clone(overrides) | ||
this.parent.insertBefore(this, cloned) | ||
this.parent.insertAfter(this, cloned) | ||
return cloned | ||
} | ||
cloneAfter(overrides = {}) { | ||
cloneBefore(overrides = {}) { | ||
let cloned = this.clone(overrides) | ||
this.parent.insertAfter(this, cloned) | ||
this.parent.insertBefore(this, cloned) | ||
return cloned | ||
} | ||
replaceWith(...nodes) { | ||
if (this.parent) { | ||
let bookmark = this | ||
let foundSelf = false | ||
for (let node of nodes) { | ||
if (node === this) { | ||
foundSelf = true | ||
} else if (foundSelf) { | ||
this.parent.insertAfter(bookmark, node) | ||
bookmark = node | ||
error(message, opts = {}) { | ||
if (this.source) { | ||
let { end, start } = this.rangeBy(opts) | ||
return this.source.input.error( | ||
message, | ||
{ column: start.column, line: start.line }, | ||
{ column: end.column, line: end.line }, | ||
opts | ||
) | ||
} | ||
return new CssSyntaxError(message) | ||
} | ||
getProxyProcessor() { | ||
return { | ||
get(node, prop) { | ||
if (prop === 'proxyOf') { | ||
return node | ||
} else if (prop === 'root') { | ||
return () => node.root().toProxy() | ||
} else { | ||
this.parent.insertBefore(bookmark, node) | ||
return node[prop] | ||
} | ||
}, | ||
set(node, prop, value) { | ||
if (node[prop] === value) return true | ||
node[prop] = value | ||
if ( | ||
prop === 'prop' || | ||
prop === 'value' || | ||
prop === 'name' || | ||
prop === 'params' || | ||
prop === 'important' || | ||
/* c8 ignore next */ | ||
prop === 'text' | ||
) { | ||
node.markDirty() | ||
} | ||
return true | ||
} | ||
} | ||
} | ||
if (!foundSelf) { | ||
this.remove() | ||
markDirty() { | ||
if (this[isClean]) { | ||
this[isClean] = false | ||
let next = this | ||
while ((next = next.parent)) { | ||
next[isClean] = false | ||
} | ||
} | ||
return this | ||
} | ||
@@ -152,2 +175,31 @@ | ||
positionBy(opts, stringRepresentation) { | ||
let pos = this.source.start | ||
if (opts.index) { | ||
pos = this.positionInside(opts.index, stringRepresentation) | ||
} else if (opts.word) { | ||
stringRepresentation = this.toString() | ||
let index = stringRepresentation.indexOf(opts.word) | ||
if (index !== -1) pos = this.positionInside(index, stringRepresentation) | ||
} | ||
return pos | ||
} | ||
positionInside(index, stringRepresentation) { | ||
let string = stringRepresentation || this.toString() | ||
let column = this.source.start.column | ||
let line = this.source.start.line | ||
for (let i = 0; i < index; i++) { | ||
if (string[i] === '\n') { | ||
column = 1 | ||
line += 1 | ||
} else { | ||
column += 1 | ||
} | ||
} | ||
return { column, line } | ||
} | ||
prev() { | ||
@@ -159,9 +211,89 @@ if (!this.parent) return undefined | ||
before(add) { | ||
this.parent.insertBefore(this, add) | ||
rangeBy(opts) { | ||
let start = { | ||
column: this.source.start.column, | ||
line: this.source.start.line | ||
} | ||
let end = this.source.end | ||
? { | ||
column: this.source.end.column + 1, | ||
line: this.source.end.line | ||
} | ||
: { | ||
column: start.column + 1, | ||
line: start.line | ||
} | ||
if (opts.word) { | ||
let stringRepresentation = this.toString() | ||
let index = stringRepresentation.indexOf(opts.word) | ||
if (index !== -1) { | ||
start = this.positionInside(index, stringRepresentation) | ||
end = this.positionInside(index + opts.word.length, stringRepresentation) | ||
} | ||
} else { | ||
if (opts.start) { | ||
start = { | ||
column: opts.start.column, | ||
line: opts.start.line | ||
} | ||
} else if (opts.index) { | ||
start = this.positionInside(opts.index) | ||
} | ||
if (opts.end) { | ||
end = { | ||
column: opts.end.column, | ||
line: opts.end.line | ||
} | ||
} else if (opts.endIndex) { | ||
end = this.positionInside(opts.endIndex) | ||
} else if (opts.index) { | ||
end = this.positionInside(opts.index + 1) | ||
} | ||
} | ||
if ( | ||
end.line < start.line || | ||
(end.line === start.line && end.column <= start.column) | ||
) { | ||
end = { column: start.column + 1, line: start.line } | ||
} | ||
return { end, start } | ||
} | ||
raw(prop, defaultType) { | ||
let str = new Stringifier() | ||
return str.raw(this, prop, defaultType) | ||
} | ||
remove() { | ||
if (this.parent) { | ||
this.parent.removeChild(this) | ||
} | ||
this.parent = undefined | ||
return this | ||
} | ||
after(add) { | ||
this.parent.insertAfter(this, add) | ||
replaceWith(...nodes) { | ||
if (this.parent) { | ||
let bookmark = this | ||
let foundSelf = false | ||
for (let node of nodes) { | ||
if (node === this) { | ||
foundSelf = true | ||
} else if (foundSelf) { | ||
this.parent.insertAfter(bookmark, node) | ||
bookmark = node | ||
} else { | ||
this.parent.insertBefore(bookmark, node) | ||
} | ||
} | ||
if (!foundSelf) { | ||
this.remove() | ||
} | ||
} | ||
return this | ||
@@ -178,13 +310,2 @@ } | ||
raw(prop, defaultType) { | ||
let str = new Stringifier() | ||
return str.raw(this, prop, defaultType) | ||
} | ||
cleanRaws(keepBetween) { | ||
delete this.raws.before | ||
delete this.raws.after | ||
if (!keepBetween) delete this.raws.between | ||
} | ||
toJSON(_, inputs) { | ||
@@ -222,5 +343,5 @@ let fixed = {} | ||
fixed[name] = { | ||
end: value.end, | ||
inputId, | ||
start: value.start, | ||
end: value.end | ||
start: value.start | ||
} | ||
@@ -239,114 +360,2 @@ } else { | ||
positionInside(index) { | ||
let string = this.toString() | ||
let column = this.source.start.column | ||
let line = this.source.start.line | ||
for (let i = 0; i < index; i++) { | ||
if (string[i] === '\n') { | ||
column = 1 | ||
line += 1 | ||
} else { | ||
column += 1 | ||
} | ||
} | ||
return { line, column } | ||
} | ||
positionBy(opts) { | ||
let pos = this.source.start | ||
if (opts.index) { | ||
pos = this.positionInside(opts.index) | ||
} else if (opts.word) { | ||
let index = this.toString().indexOf(opts.word) | ||
if (index !== -1) pos = this.positionInside(index) | ||
} | ||
return pos | ||
} | ||
rangeBy(opts) { | ||
let start = { | ||
line: this.source.start.line, | ||
column: this.source.start.column | ||
} | ||
let end = this.source.end | ||
? { | ||
line: this.source.end.line, | ||
column: this.source.end.column + 1 | ||
} | ||
: { | ||
line: start.line, | ||
column: start.column + 1 | ||
} | ||
if (opts.word) { | ||
let index = this.toString().indexOf(opts.word) | ||
if (index !== -1) { | ||
start = this.positionInside(index) | ||
end = this.positionInside(index + opts.word.length) | ||
} | ||
} else { | ||
if (opts.start) { | ||
start = { | ||
line: opts.start.line, | ||
column: opts.start.column | ||
} | ||
} else if (opts.index) { | ||
start = this.positionInside(opts.index) | ||
} | ||
if (opts.end) { | ||
end = { | ||
line: opts.end.line, | ||
column: opts.end.column | ||
} | ||
} else if (opts.endIndex) { | ||
end = this.positionInside(opts.endIndex) | ||
} else if (opts.index) { | ||
end = this.positionInside(opts.index + 1) | ||
} | ||
} | ||
if ( | ||
end.line < start.line || | ||
(end.line === start.line && end.column <= start.column) | ||
) { | ||
end = { line: start.line, column: start.column + 1 } | ||
} | ||
return { start, end } | ||
} | ||
getProxyProcessor() { | ||
return { | ||
set(node, prop, value) { | ||
if (node[prop] === value) return true | ||
node[prop] = value | ||
if ( | ||
prop === 'prop' || | ||
prop === 'value' || | ||
prop === 'name' || | ||
prop === 'params' || | ||
prop === 'important' || | ||
/* c8 ignore next */ | ||
prop === 'text' | ||
) { | ||
node.markDirty() | ||
} | ||
return true | ||
}, | ||
get(node, prop) { | ||
if (prop === 'proxyOf') { | ||
return node | ||
} else if (prop === 'root') { | ||
return () => node.root().toProxy() | ||
} else { | ||
return node[prop] | ||
} | ||
} | ||
} | ||
} | ||
toProxy() { | ||
@@ -359,22 +368,15 @@ if (!this.proxyCache) { | ||
addToError(error) { | ||
error.postcssNode = this | ||
if (error.stack && this.source && /\n\s{4}at /.test(error.stack)) { | ||
let s = this.source | ||
error.stack = error.stack.replace( | ||
/\n\s{4}at /, | ||
`$&${s.input.from}:${s.start.line}:${s.start.column}$&` | ||
) | ||
} | ||
return error | ||
toString(stringifier = stringify) { | ||
if (stringifier.stringify) stringifier = stringifier.stringify | ||
let result = '' | ||
stringifier(this, i => { | ||
result += i | ||
}) | ||
return result | ||
} | ||
markDirty() { | ||
if (this[isClean]) { | ||
this[isClean] = false | ||
let next = this | ||
while ((next = next.parent)) { | ||
next[isClean] = false | ||
} | ||
} | ||
warn(result, text, opts) { | ||
let data = { node: this } | ||
for (let i in opts) data[i] = opts[i] | ||
return result.warn(text, data) | ||
} | ||
@@ -381,0 +383,0 @@ |
import { Parser } from './postcss.js' | ||
declare const parse: Parser | ||
interface Parse extends Parser { | ||
default: Parse | ||
} | ||
export default parse | ||
declare const parse: Parse | ||
export = parse |
@@ -34,47 +34,141 @@ 'use strict' | ||
this.createTokenizer() | ||
this.root.source = { input, start: { offset: 0, line: 1, column: 1 } } | ||
this.root.source = { input, start: { column: 1, line: 1, offset: 0 } } | ||
} | ||
createTokenizer() { | ||
this.tokenizer = tokenizer(this.input) | ||
} | ||
atrule(token) { | ||
let node = new AtRule() | ||
node.name = token[1].slice(1) | ||
if (node.name === '') { | ||
this.unnamedAtrule(node, token) | ||
} | ||
this.init(node, token[2]) | ||
parse() { | ||
let token | ||
let type | ||
let prev | ||
let shift | ||
let last = false | ||
let open = false | ||
let params = [] | ||
let brackets = [] | ||
while (!this.tokenizer.endOfFile()) { | ||
token = this.tokenizer.nextToken() | ||
type = token[0] | ||
switch (token[0]) { | ||
case 'space': | ||
this.spaces += token[1] | ||
break | ||
if (type === '(' || type === '[') { | ||
brackets.push(type === '(' ? ')' : ']') | ||
} else if (type === '{' && brackets.length > 0) { | ||
brackets.push('}') | ||
} else if (type === brackets[brackets.length - 1]) { | ||
brackets.pop() | ||
} | ||
case ';': | ||
this.freeSemicolon(token) | ||
if (brackets.length === 0) { | ||
if (type === ';') { | ||
node.source.end = this.getPosition(token[2]) | ||
node.source.end.offset++ | ||
this.semicolon = true | ||
break | ||
case '}': | ||
} else if (type === '{') { | ||
open = true | ||
break | ||
} else if (type === '}') { | ||
if (params.length > 0) { | ||
shift = params.length - 1 | ||
prev = params[shift] | ||
while (prev && prev[0] === 'space') { | ||
prev = params[--shift] | ||
} | ||
if (prev) { | ||
node.source.end = this.getPosition(prev[3] || prev[2]) | ||
node.source.end.offset++ | ||
} | ||
} | ||
this.end(token) | ||
break | ||
} else { | ||
params.push(token) | ||
} | ||
} else { | ||
params.push(token) | ||
} | ||
case 'comment': | ||
this.comment(token) | ||
break | ||
if (this.tokenizer.endOfFile()) { | ||
last = true | ||
break | ||
} | ||
} | ||
case 'at-word': | ||
this.atrule(token) | ||
break | ||
node.raws.between = this.spacesAndCommentsFromEnd(params) | ||
if (params.length) { | ||
node.raws.afterName = this.spacesAndCommentsFromStart(params) | ||
this.raw(node, 'params', params) | ||
if (last) { | ||
token = params[params.length - 1] | ||
node.source.end = this.getPosition(token[3] || token[2]) | ||
node.source.end.offset++ | ||
this.spaces = node.raws.between | ||
node.raws.between = '' | ||
} | ||
} else { | ||
node.raws.afterName = '' | ||
node.params = '' | ||
} | ||
case '{': | ||
this.emptyRule(token) | ||
break | ||
if (open) { | ||
node.nodes = [] | ||
this.current = node | ||
} | ||
} | ||
default: | ||
this.other(token) | ||
break | ||
checkMissedSemicolon(tokens) { | ||
let colon = this.colon(tokens) | ||
if (colon === false) return | ||
let founded = 0 | ||
let token | ||
for (let j = colon - 1; j >= 0; j--) { | ||
token = tokens[j] | ||
if (token[0] !== 'space') { | ||
founded += 1 | ||
if (founded === 2) break | ||
} | ||
} | ||
this.endFile() | ||
// If the token is a word, e.g. `!important`, `red` or any other valid property's value. | ||
// Then we need to return the colon after that word token. [3] is the "end" colon of that word. | ||
// And because we need it after that one we do +1 to get the next one. | ||
throw this.input.error( | ||
'Missed semicolon', | ||
token[0] === 'word' ? token[3] + 1 : token[2] | ||
) | ||
} | ||
colon(tokens) { | ||
let brackets = 0 | ||
let token, type, prev | ||
for (let [i, element] of tokens.entries()) { | ||
token = element | ||
type = token[0] | ||
if (type === '(') { | ||
brackets += 1 | ||
} | ||
if (type === ')') { | ||
brackets -= 1 | ||
} | ||
if (brackets === 0 && type === ':') { | ||
if (!prev) { | ||
this.doubleColon(token) | ||
} else if (prev[0] === 'word' && prev[1] === 'progid') { | ||
continue | ||
} else { | ||
return i | ||
} | ||
} | ||
prev = token | ||
} | ||
return false | ||
} | ||
comment(token) { | ||
@@ -84,2 +178,3 @@ let node = new Comment() | ||
node.source.end = this.getPosition(token[3] || token[2]) | ||
node.source.end.offset++ | ||
@@ -99,84 +194,6 @@ let text = token[1].slice(2, -2) | ||
emptyRule(token) { | ||
let node = new Rule() | ||
this.init(node, token[2]) | ||
node.selector = '' | ||
node.raws.between = '' | ||
this.current = node | ||
createTokenizer() { | ||
this.tokenizer = tokenizer(this.input) | ||
} | ||
other(start) { | ||
let end = false | ||
let type = null | ||
let colon = false | ||
let bracket = null | ||
let brackets = [] | ||
let customProperty = start[1].startsWith('--') | ||
let tokens = [] | ||
let token = start | ||
while (token) { | ||
type = token[0] | ||
tokens.push(token) | ||
if (type === '(' || type === '[') { | ||
if (!bracket) bracket = token | ||
brackets.push(type === '(' ? ')' : ']') | ||
} else if (customProperty && colon && type === '{') { | ||
if (!bracket) bracket = token | ||
brackets.push('}') | ||
} else if (brackets.length === 0) { | ||
if (type === ';') { | ||
if (colon) { | ||
this.decl(tokens, customProperty) | ||
return | ||
} else { | ||
break | ||
} | ||
} else if (type === '{') { | ||
this.rule(tokens) | ||
return | ||
} else if (type === '}') { | ||
this.tokenizer.back(tokens.pop()) | ||
end = true | ||
break | ||
} else if (type === ':') { | ||
colon = true | ||
} | ||
} else if (type === brackets[brackets.length - 1]) { | ||
brackets.pop() | ||
if (brackets.length === 0) bracket = null | ||
} | ||
token = this.tokenizer.nextToken() | ||
} | ||
if (this.tokenizer.endOfFile()) end = true | ||
if (brackets.length > 0) this.unclosedBracket(bracket) | ||
if (end && colon) { | ||
if (!customProperty) { | ||
while (tokens.length) { | ||
token = tokens[tokens.length - 1][0] | ||
if (token !== 'space' && token !== 'comment') break | ||
this.tokenizer.back(tokens.pop()) | ||
} | ||
} | ||
this.decl(tokens, customProperty) | ||
} else { | ||
this.unknownWord(tokens) | ||
} | ||
} | ||
rule(tokens) { | ||
tokens.pop() | ||
let node = new Rule() | ||
this.init(node, tokens[0][2]) | ||
node.raws.between = this.spacesAndCommentsFromEnd(tokens) | ||
this.raw(node, 'selector', tokens) | ||
this.current = node | ||
} | ||
decl(tokens, customProperty) { | ||
@@ -195,2 +212,3 @@ let node = new Declaration() | ||
) | ||
node.source.end.offset++ | ||
@@ -287,83 +305,16 @@ while (tokens[0][0] !== 'word') { | ||
atrule(token) { | ||
let node = new AtRule() | ||
node.name = token[1].slice(1) | ||
if (node.name === '') { | ||
this.unnamedAtrule(node, token) | ||
} | ||
doubleColon(token) { | ||
throw this.input.error( | ||
'Double colon', | ||
{ offset: token[2] }, | ||
{ offset: token[2] + token[1].length } | ||
) | ||
} | ||
emptyRule(token) { | ||
let node = new Rule() | ||
this.init(node, token[2]) | ||
let type | ||
let prev | ||
let shift | ||
let last = false | ||
let open = false | ||
let params = [] | ||
let brackets = [] | ||
while (!this.tokenizer.endOfFile()) { | ||
token = this.tokenizer.nextToken() | ||
type = token[0] | ||
if (type === '(' || type === '[') { | ||
brackets.push(type === '(' ? ')' : ']') | ||
} else if (type === '{' && brackets.length > 0) { | ||
brackets.push('}') | ||
} else if (type === brackets[brackets.length - 1]) { | ||
brackets.pop() | ||
} | ||
if (brackets.length === 0) { | ||
if (type === ';') { | ||
node.source.end = this.getPosition(token[2]) | ||
this.semicolon = true | ||
break | ||
} else if (type === '{') { | ||
open = true | ||
break | ||
} else if (type === '}') { | ||
if (params.length > 0) { | ||
shift = params.length - 1 | ||
prev = params[shift] | ||
while (prev && prev[0] === 'space') { | ||
prev = params[--shift] | ||
} | ||
if (prev) { | ||
node.source.end = this.getPosition(prev[3] || prev[2]) | ||
} | ||
} | ||
this.end(token) | ||
break | ||
} else { | ||
params.push(token) | ||
} | ||
} else { | ||
params.push(token) | ||
} | ||
if (this.tokenizer.endOfFile()) { | ||
last = true | ||
break | ||
} | ||
} | ||
node.raws.between = this.spacesAndCommentsFromEnd(params) | ||
if (params.length) { | ||
node.raws.afterName = this.spacesAndCommentsFromStart(params) | ||
this.raw(node, 'params', params) | ||
if (last) { | ||
token = params[params.length - 1] | ||
node.source.end = this.getPosition(token[3] || token[2]) | ||
this.spaces = node.raws.between | ||
node.raws.between = '' | ||
} | ||
} else { | ||
node.raws.afterName = '' | ||
node.params = '' | ||
} | ||
if (open) { | ||
node.nodes = [] | ||
this.current = node | ||
} | ||
node.selector = '' | ||
node.raws.between = '' | ||
this.current = node | ||
} | ||
@@ -382,2 +333,3 @@ | ||
this.current.source.end = this.getPosition(token[2]) | ||
this.current.source.end.offset++ | ||
this.current = this.current.parent | ||
@@ -395,2 +347,3 @@ } else { | ||
this.current.raws.after = (this.current.raws.after || '') + this.spaces | ||
this.root.source.end = this.getPosition(this.tokenizer.position()) | ||
} | ||
@@ -414,5 +367,5 @@ | ||
return { | ||
offset, | ||
column: pos.col, | ||
line: pos.line, | ||
column: pos.col | ||
offset | ||
} | ||
@@ -424,4 +377,4 @@ } | ||
node.source = { | ||
start: this.getPosition(offset), | ||
input: this.input | ||
input: this.input, | ||
start: this.getPosition(offset) | ||
} | ||
@@ -433,2 +386,107 @@ node.raws.before = this.spaces | ||
other(start) { | ||
let end = false | ||
let type = null | ||
let colon = false | ||
let bracket = null | ||
let brackets = [] | ||
let customProperty = start[1].startsWith('--') | ||
let tokens = [] | ||
let token = start | ||
while (token) { | ||
type = token[0] | ||
tokens.push(token) | ||
if (type === '(' || type === '[') { | ||
if (!bracket) bracket = token | ||
brackets.push(type === '(' ? ')' : ']') | ||
} else if (customProperty && colon && type === '{') { | ||
if (!bracket) bracket = token | ||
brackets.push('}') | ||
} else if (brackets.length === 0) { | ||
if (type === ';') { | ||
if (colon) { | ||
this.decl(tokens, customProperty) | ||
return | ||
} else { | ||
break | ||
} | ||
} else if (type === '{') { | ||
this.rule(tokens) | ||
return | ||
} else if (type === '}') { | ||
this.tokenizer.back(tokens.pop()) | ||
end = true | ||
break | ||
} else if (type === ':') { | ||
colon = true | ||
} | ||
} else if (type === brackets[brackets.length - 1]) { | ||
brackets.pop() | ||
if (brackets.length === 0) bracket = null | ||
} | ||
token = this.tokenizer.nextToken() | ||
} | ||
if (this.tokenizer.endOfFile()) end = true | ||
if (brackets.length > 0) this.unclosedBracket(bracket) | ||
if (end && colon) { | ||
if (!customProperty) { | ||
while (tokens.length) { | ||
token = tokens[tokens.length - 1][0] | ||
if (token !== 'space' && token !== 'comment') break | ||
this.tokenizer.back(tokens.pop()) | ||
} | ||
} | ||
this.decl(tokens, customProperty) | ||
} else { | ||
this.unknownWord(tokens) | ||
} | ||
} | ||
parse() { | ||
let token | ||
while (!this.tokenizer.endOfFile()) { | ||
token = this.tokenizer.nextToken() | ||
switch (token[0]) { | ||
case 'space': | ||
this.spaces += token[1] | ||
break | ||
case ';': | ||
this.freeSemicolon(token) | ||
break | ||
case '}': | ||
this.end(token) | ||
break | ||
case 'comment': | ||
this.comment(token) | ||
break | ||
case 'at-word': | ||
this.atrule(token) | ||
break | ||
case '{': | ||
this.emptyRule(token) | ||
break | ||
default: | ||
this.other(token) | ||
break | ||
} | ||
} | ||
this.endFile() | ||
} | ||
precheckMissedSemicolon(/* tokens */) { | ||
// Hook for Safe Parser | ||
} | ||
raw(node, prop, tokens, customProperty) { | ||
@@ -464,3 +522,3 @@ let token, type | ||
let raw = tokens.reduce((all, i) => all + i[1], '') | ||
node.raws[prop] = { value, raw } | ||
node.raws[prop] = { raw, value } | ||
} | ||
@@ -470,2 +528,13 @@ node[prop] = value | ||
rule(tokens) { | ||
tokens.pop() | ||
let node = new Rule() | ||
this.init(node, tokens[0][2]) | ||
node.raws.between = this.spacesAndCommentsFromEnd(tokens) | ||
this.raw(node, 'selector', tokens) | ||
this.current = node | ||
} | ||
spacesAndCommentsFromEnd(tokens) { | ||
@@ -482,2 +551,4 @@ let lastTokenType | ||
// Errors | ||
spacesAndCommentsFromStart(tokens) { | ||
@@ -514,32 +585,7 @@ let next | ||
colon(tokens) { | ||
let brackets = 0 | ||
let token, type, prev | ||
for (let [i, element] of tokens.entries()) { | ||
token = element | ||
type = token[0] | ||
if (type === '(') { | ||
brackets += 1 | ||
} | ||
if (type === ')') { | ||
brackets -= 1 | ||
} | ||
if (brackets === 0 && type === ':') { | ||
if (!prev) { | ||
this.doubleColon(token) | ||
} else if (prev[0] === 'word' && prev[1] === 'progid') { | ||
continue | ||
} else { | ||
return i | ||
} | ||
} | ||
prev = token | ||
} | ||
return false | ||
unclosedBlock() { | ||
let pos = this.current.source.start | ||
throw this.input.error('Unclosed block', pos.line, pos.column) | ||
} | ||
// Errors | ||
unclosedBracket(bracket) { | ||
@@ -553,10 +599,2 @@ throw this.input.error( | ||
unknownWord(tokens) { | ||
throw this.input.error( | ||
'Unknown word', | ||
{ offset: tokens[0][2] }, | ||
{ offset: tokens[0][2] + tokens[0][1].length } | ||
) | ||
} | ||
unexpectedClose(token) { | ||
@@ -570,12 +608,7 @@ throw this.input.error( | ||
unclosedBlock() { | ||
let pos = this.current.source.start | ||
throw this.input.error('Unclosed block', pos.line, pos.column) | ||
} | ||
doubleColon(token) { | ||
unknownWord(tokens) { | ||
throw this.input.error( | ||
'Double colon', | ||
{ offset: token[2] }, | ||
{ offset: token[2] + token[1].length } | ||
'Unknown word', | ||
{ offset: tokens[0][2] }, | ||
{ offset: tokens[0][2] + tokens[0][1].length } | ||
) | ||
@@ -591,30 +624,4 @@ } | ||
} | ||
precheckMissedSemicolon(/* tokens */) { | ||
// Hook for Safe Parser | ||
} | ||
checkMissedSemicolon(tokens) { | ||
let colon = this.colon(tokens) | ||
if (colon === false) return | ||
let founded = 0 | ||
let token | ||
for (let j = colon - 1; j >= 0; j--) { | ||
token = tokens[j] | ||
if (token[0] !== 'space') { | ||
founded += 1 | ||
if (founded === 2) break | ||
} | ||
} | ||
// If the token is a word, e.g. `!important`, `red` or any other valid property's value. | ||
// Then we need to return the colon after that word token. [3] is the "end" colon of that word. | ||
// And because we need it after that one we do +1 to get the next one. | ||
throw this.input.error( | ||
'Missed semicolon', | ||
token[0] === 'word' ? token[3] + 1 : token[2] | ||
) | ||
} | ||
} | ||
module.exports = Parser |
@@ -1,82 +0,41 @@ | ||
import { SourceMapGenerator, RawSourceMap } from 'source-map-js' | ||
import { RawSourceMap, SourceMapGenerator } from 'source-map-js' | ||
import AtRule, { AtRuleProps } from './at-rule.js' | ||
import Comment, { CommentProps } from './comment.js' | ||
import Container, { ContainerProps } from './container.js' | ||
import CssSyntaxError from './css-syntax-error.js' | ||
import Declaration, { DeclarationProps } from './declaration.js' | ||
import Document, { DocumentProps } from './document.js' | ||
import Input, { FilePosition } from './input.js' | ||
import LazyResult from './lazy-result.js' | ||
import list from './list.js' | ||
import Node, { | ||
Position, | ||
Source, | ||
AnyNode, | ||
ChildNode, | ||
ChildProps, | ||
NodeErrorOptions, | ||
NodeProps, | ||
ChildProps, | ||
AnyNode | ||
Position, | ||
Source | ||
} from './node.js' | ||
import Declaration, { DeclarationProps } from './declaration.js' | ||
import Container, { ContainerProps } from './container.js' | ||
import Document, { DocumentProps } from './document.js' | ||
import Warning, { WarningOptions } from './warning.js' | ||
import Comment, { CommentProps } from './comment.js' | ||
import AtRule, { AtRuleProps } from './at-rule.js' | ||
import Input, { FilePosition } from './input.js' | ||
import Processor from './processor.js' | ||
import Result, { Message } from './result.js' | ||
import Root, { RootProps } from './root.js' | ||
import Rule, { RuleProps } from './rule.js' | ||
import CssSyntaxError from './css-syntax-error.js' | ||
import list, { List } from './list.js' | ||
import LazyResult from './lazy-result.js' | ||
import Processor from './processor.js' | ||
import Warning, { WarningOptions } from './warning.js' | ||
export { | ||
NodeErrorOptions, | ||
DeclarationProps, | ||
CssSyntaxError, | ||
ContainerProps, | ||
WarningOptions, | ||
DocumentProps, | ||
FilePosition, | ||
CommentProps, | ||
AtRuleProps, | ||
Declaration, | ||
ChildProps, | ||
LazyResult, | ||
ChildNode, | ||
NodeProps, | ||
Processor, | ||
RuleProps, | ||
RootProps, | ||
Container, | ||
Position, | ||
Document, | ||
AnyNode, | ||
Warning, | ||
Message, | ||
Comment, | ||
Source, | ||
AtRule, | ||
Result, | ||
Input, | ||
Node, | ||
list, | ||
Rule, | ||
Root | ||
} | ||
export type SourceMap = SourceMapGenerator & { | ||
toJSON(): RawSourceMap | ||
} | ||
export type Helpers = { result: Result; postcss: Postcss } & Postcss | ||
type DocumentProcessor = ( | ||
document: Document, | ||
helper: Helpers | ||
helper: postcss.Helpers | ||
) => Promise<void> | void | ||
type RootProcessor = (root: Root, helper: Helpers) => Promise<void> | void | ||
type RootProcessor = (root: Root, helper: postcss.Helpers) => Promise<void> | void | ||
type DeclarationProcessor = ( | ||
decl: Declaration, | ||
helper: Helpers | ||
helper: postcss.Helpers | ||
) => Promise<void> | void | ||
type RuleProcessor = (rule: Rule, helper: Helpers) => Promise<void> | void | ||
type AtRuleProcessor = (atRule: AtRule, helper: Helpers) => Promise<void> | void | ||
type RuleProcessor = (rule: Rule, helper: postcss.Helpers) => Promise<void> | void | ||
type AtRuleProcessor = (atRule: AtRule, helper: postcss.Helpers) => Promise<void> | void | ||
type CommentProcessor = ( | ||
comment: Comment, | ||
helper: Helpers | ||
helper: postcss.Helpers | ||
) => Promise<void> | void | ||
@@ -86,38 +45,29 @@ | ||
/** | ||
* Will be called on `Document` node. | ||
* Will be called on all`AtRule` nodes. | ||
* | ||
* Will be called again on children changes. | ||
* Will be called again on node or children changes. | ||
*/ | ||
Document?: DocumentProcessor | ||
AtRule?: { [name: string]: AtRuleProcessor } | AtRuleProcessor | ||
/** | ||
* Will be called on `Document` node, when all children will be processed. | ||
* Will be called on all `AtRule` nodes, when all children will be processed. | ||
* | ||
* Will be called again on children changes. | ||
* Will be called again on node or children changes. | ||
*/ | ||
DocumentExit?: DocumentProcessor | ||
AtRuleExit?: { [name: string]: AtRuleProcessor } | AtRuleProcessor | ||
/** | ||
* Will be called on `Root` node once. | ||
*/ | ||
Once?: RootProcessor | ||
/** | ||
* Will be called on `Root` node once, when all children will be processed. | ||
*/ | ||
OnceExit?: RootProcessor | ||
/** | ||
* Will be called on `Root` node. | ||
* Will be called on all `Comment` nodes. | ||
* | ||
* Will be called again on children changes. | ||
* Will be called again on node or children changes. | ||
*/ | ||
Root?: RootProcessor | ||
Comment?: CommentProcessor | ||
/** | ||
* Will be called on `Root` node, when all children will be processed. | ||
* Will be called on all `Comment` nodes after listeners | ||
* for `Comment` event. | ||
* | ||
* Will be called again on children changes. | ||
* Will be called again on node or children changes. | ||
*/ | ||
RootExit?: RootProcessor | ||
CommentExit?: CommentProcessor | ||
@@ -130,3 +80,3 @@ /** | ||
*/ | ||
Declaration?: DeclarationProcessor | { [prop: string]: DeclarationProcessor } | ||
Declaration?: { [prop: string]: DeclarationProcessor } | DeclarationProcessor | ||
@@ -139,235 +89,262 @@ /** | ||
DeclarationExit?: | ||
| { [prop: string]: DeclarationProcessor } | ||
| DeclarationProcessor | ||
| { [prop: string]: DeclarationProcessor } | ||
/** | ||
* Will be called on all `Rule` nodes. | ||
* Will be called on `Document` node. | ||
* | ||
* Will be called again on node or children changes. | ||
* Will be called again on children changes. | ||
*/ | ||
Rule?: RuleProcessor | ||
Document?: DocumentProcessor | ||
/** | ||
* Will be called on all `Rule` nodes, when all children will be processed. | ||
* Will be called on `Document` node, when all children will be processed. | ||
* | ||
* Will be called again on node or children changes. | ||
* Will be called again on children changes. | ||
*/ | ||
RuleExit?: RuleProcessor | ||
DocumentExit?: DocumentProcessor | ||
/** | ||
* Will be called on all`AtRule` nodes. | ||
* | ||
* Will be called again on node or children changes. | ||
* Will be called on `Root` node once. | ||
*/ | ||
AtRule?: AtRuleProcessor | { [name: string]: AtRuleProcessor } | ||
Once?: RootProcessor | ||
/** | ||
* Will be called on all `AtRule` nodes, when all children will be processed. | ||
* Will be called on `Root` node once, when all children will be processed. | ||
*/ | ||
OnceExit?: RootProcessor | ||
/** | ||
* Will be called on `Root` node. | ||
* | ||
* Will be called again on node or children changes. | ||
* Will be called again on children changes. | ||
*/ | ||
AtRuleExit?: AtRuleProcessor | { [name: string]: AtRuleProcessor } | ||
Root?: RootProcessor | ||
/** | ||
* Will be called on all `Comment` nodes. | ||
* Will be called on `Root` node, when all children will be processed. | ||
* | ||
* Will be called again on node or children changes. | ||
* Will be called again on children changes. | ||
*/ | ||
Comment?: CommentProcessor | ||
RootExit?: RootProcessor | ||
/** | ||
* Will be called on all `Comment` nodes after listeners | ||
* for `Comment` event. | ||
* Will be called on all `Rule` nodes. | ||
* | ||
* Will be called again on node or children changes. | ||
*/ | ||
CommentExit?: CommentProcessor | ||
Rule?: RuleProcessor | ||
/** | ||
* Will be called when all other listeners processed the document. | ||
* Will be called on all `Rule` nodes, when all children will be processed. | ||
* | ||
* This listener will not be called again. | ||
* Will be called again on node or children changes. | ||
*/ | ||
Exit?: RootProcessor | ||
RuleExit?: RuleProcessor | ||
} | ||
export interface Plugin extends Processors { | ||
postcssPlugin: string | ||
prepare?: (result: Result) => Processors | ||
} | ||
declare namespace postcss { | ||
export { | ||
AnyNode, | ||
AtRule, | ||
AtRuleProps, | ||
ChildNode, | ||
ChildProps, | ||
Comment, | ||
CommentProps, | ||
Container, | ||
ContainerProps, | ||
CssSyntaxError, | ||
Declaration, | ||
DeclarationProps, | ||
Document, | ||
DocumentProps, | ||
FilePosition, | ||
Input, | ||
LazyResult, | ||
list, | ||
Message, | ||
Node, | ||
NodeErrorOptions, | ||
NodeProps, | ||
Position, | ||
Processor, | ||
Result, | ||
Root, | ||
RootProps, | ||
Rule, | ||
RuleProps, | ||
Source, | ||
Warning, | ||
WarningOptions | ||
} | ||
export interface PluginCreator<PluginOptions> { | ||
(opts?: PluginOptions): Plugin | Processor | ||
postcss: true | ||
} | ||
export type SourceMap = SourceMapGenerator & { | ||
toJSON(): RawSourceMap | ||
} | ||
export interface Transformer extends TransformCallback { | ||
postcssPlugin: string | ||
postcssVersion: string | ||
} | ||
export type Helpers = { postcss: Postcss; result: Result } & Postcss | ||
export interface TransformCallback { | ||
(root: Root, result: Result): Promise<void> | void | ||
} | ||
export interface Plugin extends Processors { | ||
postcssPlugin: string | ||
prepare?: (result: Result) => Processors | ||
} | ||
export interface OldPlugin<T> extends Transformer { | ||
(opts?: T): Transformer | ||
postcss: Transformer | ||
} | ||
export interface PluginCreator<PluginOptions> { | ||
(opts?: PluginOptions): Plugin | Processor | ||
postcss: true | ||
} | ||
export type AcceptedPlugin = | ||
| Plugin | ||
| PluginCreator<any> | ||
| OldPlugin<any> | ||
| TransformCallback | ||
| { | ||
postcss: TransformCallback | Processor | ||
} | ||
| Processor | ||
export interface Transformer extends TransformCallback { | ||
postcssPlugin: string | ||
postcssVersion: string | ||
} | ||
export interface Parser<RootNode = Root | Document> { | ||
( | ||
css: string | { toString(): string }, | ||
opts?: Pick<ProcessOptions, 'map' | 'from'> | ||
): RootNode | ||
} | ||
export interface TransformCallback { | ||
(root: Root, result: Result): Promise<void> | void | ||
} | ||
export interface Builder { | ||
(part: string, node?: AnyNode, type?: 'start' | 'end'): void | ||
} | ||
export interface OldPlugin<T> extends Transformer { | ||
(opts?: T): Transformer | ||
postcss: Transformer | ||
} | ||
export interface Stringifier { | ||
(node: AnyNode, builder: Builder): void | ||
} | ||
export type AcceptedPlugin = | ||
| { | ||
postcss: Processor | TransformCallback | ||
} | ||
| OldPlugin<any> | ||
| Plugin | ||
| PluginCreator<any> | ||
| Processor | ||
| TransformCallback | ||
export interface JSONHydrator { | ||
(data: object[]): Node[] | ||
(data: object): Node | ||
} | ||
export interface Parser<RootNode = Document | Root> { | ||
( | ||
css: { toString(): string } | string, | ||
opts?: Pick<ProcessOptions, 'from' | 'map'> | ||
): RootNode | ||
} | ||
export interface Syntax { | ||
/** | ||
* Function to generate AST by string. | ||
*/ | ||
parse?: Parser | ||
export interface Builder { | ||
(part: string, node?: AnyNode, type?: 'end' | 'start'): void | ||
} | ||
/** | ||
* Class to generate string by AST. | ||
*/ | ||
stringify?: Stringifier | ||
} | ||
export interface Stringifier { | ||
(node: AnyNode, builder: Builder): void | ||
} | ||
export interface SourceMapOptions { | ||
/** | ||
* Indicates that the source map should be embedded in the output CSS | ||
* as a Base64-encoded comment. By default, it is `true`. | ||
* But if all previous maps are external, not inline, PostCSS will not embed | ||
* the map even if you do not set this option. | ||
* | ||
* If you have an inline source map, the result.map property will be empty, | ||
* as the source map will be contained within the text of `result.css`. | ||
*/ | ||
inline?: boolean | ||
export interface JSONHydrator { | ||
(data: object): Node | ||
(data: object[]): Node[] | ||
} | ||
/** | ||
* Source map content from a previous processing step (e.g., Sass). | ||
* | ||
* PostCSS will try to read the previous source map | ||
* automatically (based on comments within the source CSS), but you can use | ||
* this option to identify it manually. | ||
* | ||
* If desired, you can omit the previous map with prev: `false`. | ||
*/ | ||
prev?: string | boolean | object | ((file: string) => string) | ||
export interface Syntax<RootNode = Document | Root> { | ||
/** | ||
* Function to generate AST by string. | ||
*/ | ||
parse?: Parser<RootNode> | ||
/** | ||
* Indicates that PostCSS should set the origin content (e.g., Sass source) | ||
* of the source map. By default, it is true. But if all previous maps do not | ||
* contain sources content, PostCSS will also leave it out even if you | ||
* do not set this option. | ||
*/ | ||
sourcesContent?: boolean | ||
/** | ||
* Class to generate string by AST. | ||
*/ | ||
stringify?: Stringifier | ||
} | ||
/** | ||
* Indicates that PostCSS should add annotation comments to the CSS. | ||
* By default, PostCSS will always add a comment with a path | ||
* to the source map. PostCSS will not add annotations to CSS files | ||
* that do not contain any comments. | ||
* | ||
* By default, PostCSS presumes that you want to save the source map as | ||
* `opts.to + '.map'` and will use this path in the annotation comment. | ||
* A different path can be set by providing a string value for annotation. | ||
* | ||
* If you have set `inline: true`, annotation cannot be disabled. | ||
*/ | ||
annotation?: string | boolean | ((file: string, root: Root) => string) | ||
export interface SourceMapOptions { | ||
/** | ||
* Use absolute path in generated source map. | ||
*/ | ||
absolute?: boolean | ||
/** | ||
* Override `from` in map’s sources. | ||
*/ | ||
from?: string | ||
/** | ||
* Indicates that PostCSS should add annotation comments to the CSS. | ||
* By default, PostCSS will always add a comment with a path | ||
* to the source map. PostCSS will not add annotations to CSS files | ||
* that do not contain any comments. | ||
* | ||
* By default, PostCSS presumes that you want to save the source map as | ||
* `opts.to + '.map'` and will use this path in the annotation comment. | ||
* A different path can be set by providing a string value for annotation. | ||
* | ||
* If you have set `inline: true`, annotation cannot be disabled. | ||
*/ | ||
annotation?: ((file: string, root: Root) => string) | boolean | string | ||
/** | ||
* Use absolute path in generated source map. | ||
*/ | ||
absolute?: boolean | ||
} | ||
/** | ||
* Override `from` in map’s sources. | ||
*/ | ||
from?: string | ||
export interface ProcessOptions { | ||
/** | ||
* The path of the CSS source file. You should always set `from`, | ||
* because it is used in source map generation and syntax error messages. | ||
*/ | ||
from?: string | ||
/** | ||
* Indicates that the source map should be embedded in the output CSS | ||
* as a Base64-encoded comment. By default, it is `true`. | ||
* But if all previous maps are external, not inline, PostCSS will not embed | ||
* the map even if you do not set this option. | ||
* | ||
* If you have an inline source map, the result.map property will be empty, | ||
* as the source map will be contained within the text of `result.css`. | ||
*/ | ||
inline?: boolean | ||
/** | ||
* The path where you'll put the output CSS file. You should always set `to` | ||
* to generate correct source maps. | ||
*/ | ||
to?: string | ||
/** | ||
* Source map content from a previous processing step (e.g., Sass). | ||
* | ||
* PostCSS will try to read the previous source map | ||
* automatically (based on comments within the source CSS), but you can use | ||
* this option to identify it manually. | ||
* | ||
* If desired, you can omit the previous map with prev: `false`. | ||
*/ | ||
prev?: ((file: string) => string) | boolean | object | string | ||
/** | ||
* Function to generate AST by string. | ||
*/ | ||
parser?: Syntax | Parser | ||
/** | ||
* Indicates that PostCSS should set the origin content (e.g., Sass source) | ||
* of the source map. By default, it is true. But if all previous maps do not | ||
* contain sources content, PostCSS will also leave it out even if you | ||
* do not set this option. | ||
*/ | ||
sourcesContent?: boolean | ||
} | ||
/** | ||
* Class to generate string by AST. | ||
*/ | ||
stringifier?: Syntax | Stringifier | ||
export interface ProcessOptions<RootNode = Document | Root> { | ||
/** | ||
* The path of the CSS source file. You should always set `from`, | ||
* because it is used in source map generation and syntax error messages. | ||
*/ | ||
from?: string | ||
/** | ||
* Object with parse and stringify. | ||
*/ | ||
syntax?: Syntax | ||
/** | ||
* Source map options | ||
*/ | ||
map?: boolean | SourceMapOptions | ||
/** | ||
* Source map options | ||
*/ | ||
map?: SourceMapOptions | boolean | ||
} | ||
/** | ||
* Function to generate AST by string. | ||
*/ | ||
parser?: Parser<RootNode> | Syntax<RootNode> | ||
export interface Postcss { | ||
/** | ||
* Create a new `Processor` instance that will apply `plugins` | ||
* as CSS processors. | ||
* | ||
* ```js | ||
* let postcss = require('postcss') | ||
* | ||
* postcss(plugins).process(css, { from, to }).then(result => { | ||
* console.log(result.css) | ||
* }) | ||
* ``` | ||
* | ||
* @param plugins PostCSS plugins. | ||
* @return Processor to process multiple CSS. | ||
*/ | ||
(plugins?: AcceptedPlugin[]): Processor | ||
(...plugins: AcceptedPlugin[]): Processor | ||
/** | ||
* Class to generate string by AST. | ||
*/ | ||
stringifier?: Stringifier | Syntax<RootNode> | ||
/** | ||
* Object with parse and stringify. | ||
*/ | ||
syntax?: Syntax<RootNode> | ||
/** | ||
* The path where you'll put the output CSS file. You should always set `to` | ||
* to generate correct source maps. | ||
*/ | ||
to?: string | ||
} | ||
export type Postcss = typeof postcss | ||
/** | ||
* Default function to convert a node tree into a CSS string. | ||
*/ | ||
stringify: Stringifier | ||
export let stringify: Stringifier | ||
@@ -385,3 +362,3 @@ /** | ||
*/ | ||
parse: Parser<Root> | ||
export let parse: Parser<Root> | ||
@@ -397,10 +374,5 @@ /** | ||
*/ | ||
fromJSON: JSONHydrator | ||
export let fromJSON: JSONHydrator | ||
/** | ||
* Contains the `list` module. | ||
*/ | ||
list: List | ||
/** | ||
* Creates a new `Comment` node. | ||
@@ -411,3 +383,3 @@ * | ||
*/ | ||
comment(defaults?: CommentProps): Comment | ||
export function comment(defaults?: CommentProps): Comment | ||
@@ -420,3 +392,3 @@ /** | ||
*/ | ||
atRule(defaults?: AtRuleProps): AtRule | ||
export function atRule(defaults?: AtRuleProps): AtRule | ||
@@ -429,3 +401,3 @@ /** | ||
*/ | ||
decl(defaults?: DeclarationProps): Declaration | ||
export function decl(defaults?: DeclarationProps): Declaration | ||
@@ -438,3 +410,3 @@ /** | ||
*/ | ||
rule(defaults?: RuleProps): Rule | ||
export function rule(defaults?: RuleProps): Rule | ||
@@ -447,3 +419,3 @@ /** | ||
*/ | ||
root(defaults?: RootProps): Root | ||
export function root(defaults?: RootProps): Root | ||
@@ -456,29 +428,25 @@ /** | ||
*/ | ||
document(defaults?: DocumentProps): Document | ||
export function document(defaults?: DocumentProps): Document | ||
CssSyntaxError: typeof CssSyntaxError | ||
Declaration: typeof Declaration | ||
Container: typeof Container | ||
Comment: typeof Comment | ||
Warning: typeof Warning | ||
AtRule: typeof AtRule | ||
Result: typeof Result | ||
Input: typeof Input | ||
Rule: typeof Rule | ||
Root: typeof Root | ||
Node: typeof Node | ||
export { postcss as default } | ||
} | ||
export const stringify: Stringifier | ||
export const parse: Parser<Root> | ||
export const fromJSON: JSONHydrator | ||
/** | ||
* Create a new `Processor` instance that will apply `plugins` | ||
* as CSS processors. | ||
* | ||
* ```js | ||
* let postcss = require('postcss') | ||
* | ||
* postcss(plugins).process(css, { from, to }).then(result => { | ||
* console.log(result.css) | ||
* }) | ||
* ``` | ||
* | ||
* @param plugins PostCSS plugins. | ||
* @return Processor to process multiple CSS. | ||
*/ | ||
declare function postcss(plugins?: postcss.AcceptedPlugin[]): Processor | ||
declare function postcss(...plugins: postcss.AcceptedPlugin[]): Processor | ||
export const comment: Postcss['comment'] | ||
export const atRule: Postcss['atRule'] | ||
export const decl: Postcss['decl'] | ||
export const rule: Postcss['rule'] | ||
export const root: Postcss['root'] | ||
declare const postcss: Postcss | ||
export default postcss | ||
export = postcss |
@@ -5,2 +5,7 @@ import { SourceMapConsumer } from 'source-map-js' | ||
declare namespace PreviousMap { | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { PreviousMap_ as default } | ||
} | ||
/** | ||
@@ -18,33 +23,33 @@ * Source map information from input CSS. | ||
*/ | ||
export default class PreviousMap { | ||
declare class PreviousMap_ { | ||
/** | ||
* Was source map inlined by data-uri to input CSS. | ||
* `sourceMappingURL` content. | ||
*/ | ||
inline: boolean | ||
annotation?: string | ||
/** | ||
* `sourceMappingURL` content. | ||
* The CSS source identifier. Contains `Input#file` if the user | ||
* set the `from` option, or `Input#id` if they did not. | ||
*/ | ||
annotation?: string | ||
file?: string | ||
/** | ||
* Source map file content. | ||
* Was source map inlined by data-uri to input CSS. | ||
*/ | ||
text?: string | ||
inline: boolean | ||
/** | ||
* The directory with source map file, if source map is in separated file. | ||
* Path to source map file. | ||
*/ | ||
root?: string | ||
mapFile?: string | ||
/** | ||
* The CSS source identifier. Contains `Input#file` if the user | ||
* set the `from` option, or `Input#id` if they did not. | ||
* The directory with source map file, if source map is in separated file. | ||
*/ | ||
file?: string | ||
root?: string | ||
/** | ||
* Path to source map file. | ||
* Source map file content. | ||
*/ | ||
mapFile?: string | ||
text?: string | ||
@@ -75,1 +80,5 @@ /** | ||
} | ||
declare class PreviousMap extends PreviousMap_ {} | ||
export = PreviousMap |
@@ -38,12 +38,18 @@ 'use strict' | ||
withContent() { | ||
return !!( | ||
this.consumer().sourcesContent && | ||
this.consumer().sourcesContent.length > 0 | ||
) | ||
} | ||
decodeInline(text) { | ||
let baseCharsetUri = /^data:application\/json;charset=utf-?8;base64,/ | ||
let baseUri = /^data:application\/json;base64,/ | ||
let charsetUri = /^data:application\/json;charset=utf-?8,/ | ||
let uri = /^data:application\/json,/ | ||
startWith(string, start) { | ||
if (!string) return false | ||
return string.substr(0, start.length) === start | ||
if (charsetUri.test(text) || uri.test(text)) { | ||
return decodeURIComponent(text.substr(RegExp.lastMatch.length)) | ||
} | ||
if (baseCharsetUri.test(text) || baseUri.test(text)) { | ||
return fromBase64(text.substr(RegExp.lastMatch.length)) | ||
} | ||
let encoding = text.match(/data:application\/json;([^,]+),/)[1] | ||
throw new Error('Unsupported source map encoding ' + encoding) | ||
} | ||
@@ -55,2 +61,11 @@ | ||
isMap(map) { | ||
if (typeof map !== 'object') return false | ||
return ( | ||
typeof map.mappings === 'string' || | ||
typeof map._mappings === 'string' || | ||
Array.isArray(map.sections) | ||
) | ||
} | ||
loadAnnotation(css) { | ||
@@ -70,20 +85,2 @@ let comments = css.match(/\/\*\s*# sourceMappingURL=/gm) | ||
decodeInline(text) { | ||
let baseCharsetUri = /^data:application\/json;charset=utf-?8;base64,/ | ||
let baseUri = /^data:application\/json;base64,/ | ||
let charsetUri = /^data:application\/json;charset=utf-?8,/ | ||
let uri = /^data:application\/json,/ | ||
if (charsetUri.test(text) || uri.test(text)) { | ||
return decodeURIComponent(text.substr(RegExp.lastMatch.length)) | ||
} | ||
if (baseCharsetUri.test(text) || baseUri.test(text)) { | ||
return fromBase64(text.substr(RegExp.lastMatch.length)) | ||
} | ||
let encoding = text.match(/data:application\/json;([^,]+),/)[1] | ||
throw new Error('Unsupported source map encoding ' + encoding) | ||
} | ||
loadFile(path) { | ||
@@ -134,8 +131,11 @@ this.root = dirname(path) | ||
isMap(map) { | ||
if (typeof map !== 'object') return false | ||
return ( | ||
typeof map.mappings === 'string' || | ||
typeof map._mappings === 'string' || | ||
Array.isArray(map.sections) | ||
startWith(string, start) { | ||
if (!string) return false | ||
return string.substr(0, start.length) === start | ||
} | ||
withContent() { | ||
return !!( | ||
this.consumer().sourcesContent && | ||
this.consumer().sourcesContent.length > 0 | ||
) | ||
@@ -142,0 +142,0 @@ } |
@@ -0,1 +1,4 @@ | ||
import Document from './document.js' | ||
import LazyResult from './lazy-result.js' | ||
import NoWorkResult from './no-work-result.js' | ||
import { | ||
@@ -5,10 +8,13 @@ AcceptedPlugin, | ||
ProcessOptions, | ||
Transformer, | ||
TransformCallback | ||
TransformCallback, | ||
Transformer | ||
} from './postcss.js' | ||
import LazyResult from './lazy-result.js' | ||
import Result from './result.js' | ||
import Root from './root.js' | ||
import NoWorkResult from './no-work-result.js' | ||
declare namespace Processor { | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Processor_ as default } | ||
} | ||
/** | ||
@@ -24,4 +30,14 @@ * Contains plugins to process CSS. Create one `Processor` instance, | ||
*/ | ||
export default class Processor { | ||
declare class Processor_ { | ||
/** | ||
* Plugins added to this processor. | ||
* | ||
* ```js | ||
* const processor = postcss([autoprefixer, postcssNested]) | ||
* processor.plugins.length //=> 2 | ||
* ``` | ||
*/ | ||
plugins: (Plugin | TransformCallback | Transformer)[] | ||
/** | ||
* Current PostCSS version. | ||
@@ -38,17 +54,34 @@ * | ||
/** | ||
* Plugins added to this processor. | ||
* @param plugins PostCSS plugins | ||
*/ | ||
constructor(plugins?: AcceptedPlugin[]) | ||
/** | ||
* Parses source CSS and returns a `LazyResult` Promise proxy. | ||
* Because some plugins can be asynchronous it doesn’t make | ||
* any transformations. Transformations will be applied | ||
* in the `LazyResult` methods. | ||
* | ||
* ```js | ||
* const processor = postcss([autoprefixer, postcssNested]) | ||
* processor.plugins.length //=> 2 | ||
* processor.process(css, { from: 'a.css', to: 'a.out.css' }) | ||
* .then(result => { | ||
* console.log(result.css) | ||
* }) | ||
* ``` | ||
* | ||
* @param css String with input CSS or any object with a `toString()` method, | ||
* like a Buffer. Optionally, send a `Result` instance | ||
* and the processor will take the `Root` from it. | ||
* @param opts Options. | ||
* @return Promise proxy. | ||
*/ | ||
plugins: (Plugin | Transformer | TransformCallback)[] | ||
process( | ||
css: { toString(): string } | LazyResult | Result | Root | string | ||
): LazyResult | NoWorkResult | ||
process<RootNode extends Document | Root = Root>( | ||
css: { toString(): string } | LazyResult | Result | Root | string, | ||
options: ProcessOptions<RootNode> | ||
): LazyResult<RootNode> | ||
/** | ||
* @param plugins PostCSS plugins | ||
*/ | ||
constructor(plugins?: AcceptedPlugin[]) | ||
/** | ||
* Adds a plugin to be used as a CSS processor. | ||
@@ -60,3 +93,3 @@ * | ||
* PostCSS will call this function without argument to get plugin. | ||
* * A function. PostCSS will pass the function a @{link Root} | ||
* * A function. PostCSS will pass the function a {@link Root} | ||
* as the first argument and current `Result` instance | ||
@@ -82,26 +115,6 @@ * as the second. | ||
use(plugin: AcceptedPlugin): this | ||
} | ||
/** | ||
* Parses source CSS and returns a `LazyResult` Promise proxy. | ||
* Because some plugins can be asynchronous it doesn’t make | ||
* any transformations. Transformations will be applied | ||
* in the `LazyResult` methods. | ||
* | ||
* ```js | ||
* processor.process(css, { from: 'a.css', to: 'a.out.css' }) | ||
* .then(result => { | ||
* console.log(result.css) | ||
* }) | ||
* ``` | ||
* | ||
* @param css String with input CSS or any object with a `toString()` method, | ||
* like a Buffer. Optionally, senda `Result` instance | ||
* and the processor will take the `Root` from it. | ||
* @param opts Options. | ||
* @return Promise proxy. | ||
*/ | ||
process( | ||
css: string | { toString(): string } | Result | LazyResult | Root, | ||
options?: ProcessOptions | ||
): LazyResult | NoWorkResult | ||
} | ||
declare class Processor extends Processor_ {} | ||
export = Processor |
@@ -10,24 +10,6 @@ 'use strict' | ||
constructor(plugins = []) { | ||
this.version = '8.4.17' | ||
this.version = '8.4.31' | ||
this.plugins = this.normalize(plugins) | ||
} | ||
use(plugin) { | ||
this.plugins = this.plugins.concat(this.normalize([plugin])) | ||
return this | ||
} | ||
process(css, opts = {}) { | ||
if ( | ||
this.plugins.length === 0 && | ||
typeof opts.parser === 'undefined' && | ||
typeof opts.stringifier === 'undefined' && | ||
typeof opts.syntax === 'undefined' | ||
) { | ||
return new NoWorkResult(this, css, opts) | ||
} else { | ||
return new LazyResult(this, css, opts) | ||
} | ||
} | ||
normalize(plugins) { | ||
@@ -62,2 +44,20 @@ let normalized = [] | ||
} | ||
process(css, opts = {}) { | ||
if ( | ||
this.plugins.length === 0 && | ||
typeof opts.parser === 'undefined' && | ||
typeof opts.stringifier === 'undefined' && | ||
typeof opts.syntax === 'undefined' | ||
) { | ||
return new NoWorkResult(this, css, opts) | ||
} else { | ||
return new LazyResult(this, css, opts) | ||
} | ||
} | ||
use(plugin) { | ||
this.plugins = this.plugins.concat(this.normalize([plugin])) | ||
return this | ||
} | ||
} | ||
@@ -64,0 +64,0 @@ |
import { | ||
Document, | ||
Node, | ||
Plugin, | ||
ProcessOptions, | ||
Plugin, | ||
Root, | ||
SourceMap, | ||
TransformCallback, | ||
Root, | ||
Document, | ||
Node, | ||
Warning, | ||
@@ -14,27 +14,33 @@ WarningOptions | ||
export interface Message { | ||
/** | ||
* Message type. | ||
*/ | ||
type: string | ||
declare namespace Result { | ||
export interface Message { | ||
[others: string]: any | ||
/** | ||
* Source PostCSS plugin name. | ||
*/ | ||
plugin?: string | ||
/** | ||
* Source PostCSS plugin name. | ||
*/ | ||
plugin?: string | ||
[others: string]: any | ||
} | ||
/** | ||
* Message type. | ||
*/ | ||
type: string | ||
} | ||
export interface ResultOptions extends ProcessOptions { | ||
/** | ||
* The CSS node that was the source of the warning. | ||
*/ | ||
node?: Node | ||
export interface ResultOptions extends ProcessOptions { | ||
/** | ||
* The CSS node that was the source of the warning. | ||
*/ | ||
node?: Node | ||
/** | ||
* Name of plugin that created this warning. `Result#warn` will fill it | ||
* automatically with `Plugin#postcssPlugin` value. | ||
*/ | ||
plugin?: string | ||
/** | ||
* Name of plugin that created this warning. `Result#warn` will fill it | ||
* automatically with `Plugin#postcssPlugin` value. | ||
*/ | ||
plugin?: string | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Result_ as default } | ||
} | ||
@@ -58,17 +64,34 @@ | ||
*/ | ||
export default class Result { | ||
declare class Result_<RootNode = Document | Root> { | ||
/** | ||
* The Processor instance used for this transformation. | ||
* A CSS string representing of `Result#root`. | ||
* | ||
* ```js | ||
* for (const plugin of result.processor.plugins) { | ||
* if (plugin.postcssPlugin === 'postcss-bad') { | ||
* throw 'postcss-good is incompatible with postcss-bad' | ||
* } | ||
* }) | ||
* postcss.parse('a{}').toResult().css //=> "a{}" | ||
* ``` | ||
*/ | ||
processor: Processor | ||
css: string | ||
/** | ||
* Last runned PostCSS plugin. | ||
*/ | ||
lastPlugin: Plugin | TransformCallback | ||
/** | ||
* An instance of `SourceMapGenerator` class from the `source-map` library, | ||
* representing changes to the `Result#root` instance. | ||
* | ||
* ```js | ||
* result.map.toJSON() //=> { version: 3, file: 'a.css', … } | ||
* ``` | ||
* | ||
* ```js | ||
* if (result.map) { | ||
* fs.writeFileSync(result.opts.to + '.map', result.map.toString()) | ||
* } | ||
* ``` | ||
*/ | ||
map: SourceMap | ||
/** | ||
* Contains messages from plugins (e.g., warnings or custom messages). | ||
@@ -91,14 +114,5 @@ * Each message should have type and plugin properties. | ||
*/ | ||
messages: Message[] | ||
messages: Result.Message[] | ||
/** | ||
* Root node after all transformations. | ||
* | ||
* ```js | ||
* root.toResult().root === root | ||
* ``` | ||
*/ | ||
root: Root | Document | ||
/** | ||
* Options from the `Processor#process` or `Root#toResult` call | ||
@@ -111,35 +125,27 @@ * that produced this Result instance.] | ||
*/ | ||
opts: ResultOptions | ||
opts: Result.ResultOptions | ||
/** | ||
* A CSS string representing of `Result#root`. | ||
* The Processor instance used for this transformation. | ||
* | ||
* ```js | ||
* postcss.parse('a{}').toResult().css //=> "a{}" | ||
* for (const plugin of result.processor.plugins) { | ||
* if (plugin.postcssPlugin === 'postcss-bad') { | ||
* throw 'postcss-good is incompatible with postcss-bad' | ||
* } | ||
* }) | ||
* ``` | ||
*/ | ||
css: string | ||
processor: Processor | ||
/** | ||
* An instance of `SourceMapGenerator` class from the `source-map` library, | ||
* representing changes to the `Result#root` instance. | ||
* Root node after all transformations. | ||
* | ||
* ```js | ||
* result.map.toJSON() //=> { version: 3, file: 'a.css', … } | ||
* root.toResult().root === root | ||
* ``` | ||
* | ||
* ```js | ||
* if (result.map) { | ||
* fs.writeFileSync(result.opts.to + '.map', result.map.toString()) | ||
* } | ||
* ``` | ||
*/ | ||
map: SourceMap | ||
root: RootNode | ||
/** | ||
* Last runned PostCSS plugin. | ||
*/ | ||
lastPlugin: Plugin | TransformCallback | ||
/** | ||
* @param processor Processor used for this transformation. | ||
@@ -149,15 +155,5 @@ * @param root Root node after all transformations. | ||
*/ | ||
constructor(processor: Processor, root: Root | Document, opts: ResultOptions) | ||
constructor(processor: Processor, root: RootNode, opts: Result.ResultOptions) | ||
/** | ||
* An alias for the `Result#css` property. | ||
* Use it with syntaxes that generate non-CSS output. | ||
* | ||
* ```js | ||
* result.css === result.content | ||
* ``` | ||
*/ | ||
get content(): string | ||
/** | ||
* Returns for `Result#css` content. | ||
@@ -201,2 +197,16 @@ * | ||
warnings(): Warning[] | ||
/** | ||
* An alias for the `Result#css` property. | ||
* Use it with syntaxes that generate non-CSS output. | ||
* | ||
* ```js | ||
* result.css === result.content | ||
* ``` | ||
*/ | ||
get content(): string | ||
} | ||
declare class Result<RootNode = Document | Root> extends Result_<RootNode> {} | ||
export = Result |
@@ -6,36 +6,41 @@ import Container, { ContainerProps } from './container.js' | ||
interface RootRaws extends Record<string, any> { | ||
/** | ||
* The space symbols after the last child to the end of file. | ||
*/ | ||
after?: string | ||
declare namespace Root { | ||
export interface RootRaws extends Record<string, any> { | ||
/** | ||
* The space symbols after the last child to the end of file. | ||
*/ | ||
after?: string | ||
/** | ||
* Non-CSS code before `Root`, when `Root` is inside `Document`. | ||
* | ||
* **Experimental:** some aspects of this node could change within minor | ||
* or patch version releases. | ||
*/ | ||
codeBefore?: string | ||
/** | ||
* Non-CSS code after `Root`, when `Root` is inside `Document`. | ||
* | ||
* **Experimental:** some aspects of this node could change within minor | ||
* or patch version releases. | ||
*/ | ||
codeAfter?: string | ||
/** | ||
* Non-CSS code after `Root`, when `Root` is inside `Document`. | ||
* | ||
* **Experimental:** some aspects of this node could change within minor | ||
* or patch version releases. | ||
*/ | ||
codeAfter?: string | ||
/** | ||
* Non-CSS code before `Root`, when `Root` is inside `Document`. | ||
* | ||
* **Experimental:** some aspects of this node could change within minor | ||
* or patch version releases. | ||
*/ | ||
codeBefore?: string | ||
/** | ||
* Is the last child has an (optional) semicolon. | ||
*/ | ||
semicolon?: boolean | ||
} | ||
/** | ||
* Is the last child has an (optional) semicolon. | ||
*/ | ||
semicolon?: boolean | ||
} | ||
export interface RootProps extends ContainerProps { | ||
/** | ||
* Information used to generate byte-to-byte equal node string | ||
* as it was in the origin input. | ||
* */ | ||
raws?: RootRaws | ||
export interface RootProps extends ContainerProps { | ||
/** | ||
* Information used to generate byte-to-byte equal node string | ||
* as it was in the origin input. | ||
* */ | ||
raws?: RootRaws | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Root_ as default } | ||
} | ||
@@ -52,7 +57,14 @@ | ||
*/ | ||
export default class Root extends Container { | ||
declare class Root_ extends Container { | ||
parent: Document | undefined | ||
raws: Root.RootRaws | ||
type: 'root' | ||
parent: Document | undefined | ||
raws: RootRaws | ||
constructor(defaults?: Root.RootProps) | ||
assign(overrides: object | Root.RootProps): this | ||
clone(overrides?: Partial<Root.RootProps>): Root | ||
cloneAfter(overrides?: Partial<Root.RootProps>): Root | ||
cloneBefore(overrides?: Partial<Root.RootProps>): Root | ||
/** | ||
@@ -72,5 +84,6 @@ * Returns a `Result` instance representing the root’s CSS. | ||
toResult(options?: ProcessOptions): Result | ||
} | ||
constructor(defaults?: RootProps) | ||
assign(overrides: object | RootProps): this | ||
} | ||
declare class Root extends Root_ {} | ||
export = Root |
@@ -14,12 +14,2 @@ 'use strict' | ||
removeChild(child, ignore) { | ||
let index = this.index(child) | ||
if (!ignore && index === 0 && this.nodes.length > 1) { | ||
this.nodes[1].raws.before = this.nodes[index].raws.before | ||
} | ||
return super.removeChild(child) | ||
} | ||
normalize(child, sample, type) { | ||
@@ -45,2 +35,12 @@ let nodes = super.normalize(child) | ||
removeChild(child, ignore) { | ||
let index = this.index(child) | ||
if (!ignore && index === 0 && this.nodes.length > 1) { | ||
this.nodes[1].raws.before = this.nodes[index].raws.before | ||
} | ||
return super.removeChild(child) | ||
} | ||
toResult(opts = {}) { | ||
@@ -47,0 +47,0 @@ let lazy = new LazyResult(new Processor(), this, opts) |
import Container, { ContainerProps } from './container.js' | ||
interface RuleRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
*/ | ||
before?: string | ||
declare namespace Rule { | ||
export interface RuleRaws extends Record<string, unknown> { | ||
/** | ||
* The space symbols after the last child of the node to the end of the node. | ||
*/ | ||
after?: string | ||
/** | ||
* The space symbols after the last child of the node to the end of the node. | ||
*/ | ||
after?: string | ||
/** | ||
* The space symbols before the node. It also stores `*` | ||
* and `_` symbols before the declaration (IE hack). | ||
*/ | ||
before?: string | ||
/** | ||
* The symbols between the selector and `{` for rules. | ||
*/ | ||
between?: string | ||
/** | ||
* The symbols between the selector and `{` for rules. | ||
*/ | ||
between?: string | ||
/** | ||
* Contains `true` if the last child has an (optional) semicolon. | ||
*/ | ||
semicolon?: boolean | ||
/** | ||
* Contains `true` if there is semicolon after rule. | ||
*/ | ||
ownSemicolon?: string | ||
/** | ||
* Contains `true` if there is semicolon after rule. | ||
*/ | ||
ownSemicolon?: string | ||
/** | ||
* The rule’s selector with comments. | ||
*/ | ||
selector?: { | ||
raw: string | ||
value: string | ||
} | ||
/** | ||
* The rule’s selector with comments. | ||
*/ | ||
selector?: { | ||
value: string | ||
raw: string | ||
/** | ||
* Contains `true` if the last child has an (optional) semicolon. | ||
*/ | ||
semicolon?: boolean | ||
} | ||
} | ||
export interface RuleProps extends ContainerProps { | ||
/** Selector or selectors of the rule. */ | ||
selector?: string | ||
/** Selectors of the rule represented as an array of strings. */ | ||
selectors?: string[] | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: RuleRaws | ||
export interface RuleProps extends ContainerProps { | ||
/** Information used to generate byte-to-byte equal node string as it was in the origin input. */ | ||
raws?: RuleRaws | ||
/** Selector or selectors of the rule. */ | ||
selector?: string | ||
/** Selectors of the rule represented as an array of strings. */ | ||
selectors?: string[] | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Rule_ as default } | ||
} | ||
@@ -66,7 +71,5 @@ | ||
*/ | ||
export default class Rule extends Container { | ||
type: 'rule' | ||
declare class Rule_ extends Container { | ||
parent: Container | undefined | ||
raws: RuleRaws | ||
raws: Rule.RuleRaws | ||
/** | ||
@@ -100,7 +103,13 @@ * The rule’s full selector represented as a string. | ||
constructor(defaults?: RuleProps) | ||
assign(overrides: object | RuleProps): this | ||
clone(overrides?: Partial<RuleProps>): this | ||
cloneBefore(overrides?: Partial<RuleProps>): this | ||
cloneAfter(overrides?: Partial<RuleProps>): this | ||
type: 'rule' | ||
constructor(defaults?: Rule.RuleProps) | ||
assign(overrides: object | Rule.RuleProps): this | ||
clone(overrides?: Partial<Rule.RuleProps>): Rule | ||
cloneAfter(overrides?: Partial<Rule.RuleProps>): Rule | ||
cloneBefore(overrides?: Partial<Rule.RuleProps>): Rule | ||
} | ||
declare class Rule extends Rule_ {} | ||
export = Rule |
import { | ||
AnyNode, | ||
AtRule, | ||
Builder, | ||
Comment, | ||
Container, | ||
Declaration, | ||
Document, | ||
Root, | ||
Comment, | ||
Declaration, | ||
Builder, | ||
AnyNode, | ||
Rule, | ||
AtRule, | ||
Container | ||
Rule | ||
} from './postcss.js' | ||
export default class Stringifier { | ||
declare namespace Stringifier { | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Stringifier_ as default } | ||
} | ||
declare class Stringifier_ { | ||
builder: Builder | ||
constructor(builder: Builder) | ||
stringify(node: AnyNode, semicolon?: boolean): void | ||
document(node: Document): void | ||
root(node: Root): void | ||
atrule(node: AtRule, semicolon?: boolean): void | ||
beforeAfter(node: AnyNode, detect: 'after' | 'before'): string | ||
block(node: AnyNode, start: string): void | ||
body(node: Container): void | ||
comment(node: Comment): void | ||
decl(node: Declaration, semicolon?: boolean): void | ||
rule(node: Rule): void | ||
atrule(node: AtRule, semicolon?: boolean): void | ||
body(node: Container): void | ||
block(node: AnyNode, start: string): void | ||
raw(node: AnyNode, own: string | null, detect?: string): string | ||
rawSemicolon(root: Root): boolean | undefined | ||
rawEmptyBody(root: Root): string | undefined | ||
rawIndent(root: Root): string | undefined | ||
document(node: Document): void | ||
raw(node: AnyNode, own: null | string, detect?: string): string | ||
rawBeforeClose(root: Root): string | undefined | ||
rawBeforeComment(root: Root, node: Comment): string | undefined | ||
rawBeforeDecl(root: Root, node: Declaration): string | undefined | ||
rawBeforeOpen(root: Root): string | undefined | ||
rawBeforeRule(root: Root): string | undefined | ||
rawBeforeClose(root: Root): string | undefined | ||
rawBeforeOpen(root: Root): string | undefined | ||
rawColon(root: Root): string | undefined | ||
beforeAfter(node: AnyNode, detect: 'before' | 'after'): string | ||
rawEmptyBody(root: Root): string | undefined | ||
rawIndent(root: Root): string | undefined | ||
rawSemicolon(root: Root): boolean | undefined | ||
rawValue(node: AnyNode, prop: string): string | ||
root(node: Root): void | ||
rule(node: Rule): void | ||
stringify(node: AnyNode, semicolon?: boolean): void | ||
} | ||
declare class Stringifier extends Stringifier_ {} | ||
export = Stringifier |
'use strict' | ||
const DEFAULT_RAW = { | ||
colon: ': ', | ||
indent: ' ', | ||
after: '\n', | ||
beforeClose: '\n', | ||
beforeComment: '\n', | ||
beforeDecl: '\n', | ||
beforeOpen: ' ', | ||
beforeRule: '\n', | ||
beforeOpen: ' ', | ||
beforeClose: '\n', | ||
beforeComment: '\n', | ||
after: '\n', | ||
emptyBody: '', | ||
colon: ': ', | ||
commentLeft: ' ', | ||
commentRight: ' ', | ||
emptyBody: '', | ||
indent: ' ', | ||
semicolon: false | ||
@@ -27,50 +27,2 @@ } | ||
stringify(node, semicolon) { | ||
/* c8 ignore start */ | ||
if (!this[node.type]) { | ||
throw new Error( | ||
'Unknown AST node type ' + | ||
node.type + | ||
'. ' + | ||
'Maybe you need to change PostCSS stringifier.' | ||
) | ||
} | ||
/* c8 ignore stop */ | ||
this[node.type](node, semicolon) | ||
} | ||
document(node) { | ||
this.body(node) | ||
} | ||
root(node) { | ||
this.body(node) | ||
if (node.raws.after) this.builder(node.raws.after) | ||
} | ||
comment(node) { | ||
let left = this.raw(node, 'left', 'commentLeft') | ||
let right = this.raw(node, 'right', 'commentRight') | ||
this.builder('/*' + left + node.text + right + '*/', node) | ||
} | ||
decl(node, semicolon) { | ||
let between = this.raw(node, 'between', 'colon') | ||
let string = node.prop + between + this.rawValue(node, 'value') | ||
if (node.important) { | ||
string += node.raws.important || ' !important' | ||
} | ||
if (semicolon) string += ';' | ||
this.builder(string, node) | ||
} | ||
rule(node) { | ||
this.block(node, this.rawValue(node, 'selector')) | ||
if (node.raws.ownSemicolon) { | ||
this.builder(node.raws.ownSemicolon, node, 'end') | ||
} | ||
} | ||
atrule(node, semicolon) { | ||
@@ -94,16 +46,29 @@ let name = '@' + node.name | ||
body(node) { | ||
let last = node.nodes.length - 1 | ||
while (last > 0) { | ||
if (node.nodes[last].type !== 'comment') break | ||
last -= 1 | ||
beforeAfter(node, detect) { | ||
let value | ||
if (node.type === 'decl') { | ||
value = this.raw(node, null, 'beforeDecl') | ||
} else if (node.type === 'comment') { | ||
value = this.raw(node, null, 'beforeComment') | ||
} else if (detect === 'before') { | ||
value = this.raw(node, null, 'beforeRule') | ||
} else { | ||
value = this.raw(node, null, 'beforeClose') | ||
} | ||
let semicolon = this.raw(node, 'semicolon') | ||
for (let i = 0; i < node.nodes.length; i++) { | ||
let child = node.nodes[i] | ||
let before = this.raw(child, 'before') | ||
if (before) this.builder(before) | ||
this.stringify(child, last !== i || semicolon) | ||
let buf = node.parent | ||
let depth = 0 | ||
while (buf && buf.type !== 'root') { | ||
depth += 1 | ||
buf = buf.parent | ||
} | ||
if (value.includes('\n')) { | ||
let indent = this.raw(node, null, 'indent') | ||
if (indent.length) { | ||
for (let step = 0; step < depth; step++) value += indent | ||
} | ||
} | ||
return value | ||
} | ||
@@ -127,2 +92,40 @@ | ||
body(node) { | ||
let last = node.nodes.length - 1 | ||
while (last > 0) { | ||
if (node.nodes[last].type !== 'comment') break | ||
last -= 1 | ||
} | ||
let semicolon = this.raw(node, 'semicolon') | ||
for (let i = 0; i < node.nodes.length; i++) { | ||
let child = node.nodes[i] | ||
let before = this.raw(child, 'before') | ||
if (before) this.builder(before) | ||
this.stringify(child, last !== i || semicolon) | ||
} | ||
} | ||
comment(node) { | ||
let left = this.raw(node, 'left', 'commentLeft') | ||
let right = this.raw(node, 'right', 'commentRight') | ||
this.builder('/*' + left + node.text + right + '*/', node) | ||
} | ||
decl(node, semicolon) { | ||
let between = this.raw(node, 'between', 'colon') | ||
let string = node.prop + between + this.rawValue(node, 'value') | ||
if (node.important) { | ||
string += node.raws.important || ' !important' | ||
} | ||
if (semicolon) string += ';' | ||
this.builder(string, node) | ||
} | ||
document(node) { | ||
this.body(node) | ||
} | ||
raw(node, own, detect) { | ||
@@ -182,34 +185,11 @@ let value | ||
rawSemicolon(root) { | ||
rawBeforeClose(root) { | ||
let value | ||
root.walk(i => { | ||
if (i.nodes && i.nodes.length && i.last.type === 'decl') { | ||
value = i.raws.semicolon | ||
if (typeof value !== 'undefined') return false | ||
} | ||
}) | ||
return value | ||
} | ||
rawEmptyBody(root) { | ||
let value | ||
root.walk(i => { | ||
if (i.nodes && i.nodes.length === 0) { | ||
value = i.raws.after | ||
if (typeof value !== 'undefined') return false | ||
} | ||
}) | ||
return value | ||
} | ||
rawIndent(root) { | ||
if (root.raws.indent) return root.raws.indent | ||
let value | ||
root.walk(i => { | ||
let p = i.parent | ||
if (p && p !== root && p.parent && p.parent === root) { | ||
if (typeof i.raws.before !== 'undefined') { | ||
let parts = i.raws.before.split('\n') | ||
value = parts[parts.length - 1] | ||
value = value.replace(/\S/g, '') | ||
if (i.nodes && i.nodes.length > 0) { | ||
if (typeof i.raws.after !== 'undefined') { | ||
value = i.raws.after | ||
if (value.includes('\n')) { | ||
value = value.replace(/[^\n]+$/, '') | ||
} | ||
return false | ||
@@ -219,2 +199,3 @@ } | ||
}) | ||
if (value) value = value.replace(/\S/g, '') | ||
return value | ||
@@ -261,2 +242,13 @@ } | ||
rawBeforeOpen(root) { | ||
let value | ||
root.walk(i => { | ||
if (i.type !== 'decl') { | ||
value = i.raws.between | ||
if (typeof value !== 'undefined') return false | ||
} | ||
}) | ||
return value | ||
} | ||
rawBeforeRule(root) { | ||
@@ -279,24 +271,18 @@ let value | ||
rawBeforeClose(root) { | ||
rawColon(root) { | ||
let value | ||
root.walk(i => { | ||
if (i.nodes && i.nodes.length > 0) { | ||
if (typeof i.raws.after !== 'undefined') { | ||
value = i.raws.after | ||
if (value.includes('\n')) { | ||
value = value.replace(/[^\n]+$/, '') | ||
} | ||
return false | ||
} | ||
root.walkDecls(i => { | ||
if (typeof i.raws.between !== 'undefined') { | ||
value = i.raws.between.replace(/[^\s:]/g, '') | ||
return false | ||
} | ||
}) | ||
if (value) value = value.replace(/\S/g, '') | ||
return value | ||
} | ||
rawBeforeOpen(root) { | ||
rawEmptyBody(root) { | ||
let value | ||
root.walk(i => { | ||
if (i.type !== 'decl') { | ||
value = i.raws.between | ||
if (i.nodes && i.nodes.length === 0) { | ||
value = i.raws.after | ||
if (typeof value !== 'undefined') return false | ||
@@ -308,8 +294,14 @@ } | ||
rawColon(root) { | ||
rawIndent(root) { | ||
if (root.raws.indent) return root.raws.indent | ||
let value | ||
root.walkDecls(i => { | ||
if (typeof i.raws.between !== 'undefined') { | ||
value = i.raws.between.replace(/[^\s:]/g, '') | ||
return false | ||
root.walk(i => { | ||
let p = i.parent | ||
if (p && p !== root && p.parent && p.parent === root) { | ||
if (typeof i.raws.before !== 'undefined') { | ||
let parts = i.raws.before.split('\n') | ||
value = parts[parts.length - 1] | ||
value = value.replace(/\S/g, '') | ||
return false | ||
} | ||
} | ||
@@ -320,28 +312,10 @@ }) | ||
beforeAfter(node, detect) { | ||
rawSemicolon(root) { | ||
let value | ||
if (node.type === 'decl') { | ||
value = this.raw(node, null, 'beforeDecl') | ||
} else if (node.type === 'comment') { | ||
value = this.raw(node, null, 'beforeComment') | ||
} else if (detect === 'before') { | ||
value = this.raw(node, null, 'beforeRule') | ||
} else { | ||
value = this.raw(node, null, 'beforeClose') | ||
} | ||
let buf = node.parent | ||
let depth = 0 | ||
while (buf && buf.type !== 'root') { | ||
depth += 1 | ||
buf = buf.parent | ||
} | ||
if (value.includes('\n')) { | ||
let indent = this.raw(node, null, 'indent') | ||
if (indent.length) { | ||
for (let step = 0; step < depth; step++) value += indent | ||
root.walk(i => { | ||
if (i.nodes && i.nodes.length && i.last.type === 'decl') { | ||
value = i.raws.semicolon | ||
if (typeof value !== 'undefined') return false | ||
} | ||
} | ||
}) | ||
return value | ||
@@ -359,2 +333,28 @@ } | ||
} | ||
root(node) { | ||
this.body(node) | ||
if (node.raws.after) this.builder(node.raws.after) | ||
} | ||
rule(node) { | ||
this.block(node, this.rawValue(node, 'selector')) | ||
if (node.raws.ownSemicolon) { | ||
this.builder(node.raws.ownSemicolon, node, 'end') | ||
} | ||
} | ||
stringify(node, semicolon) { | ||
/* c8 ignore start */ | ||
if (!this[node.type]) { | ||
throw new Error( | ||
'Unknown AST node type ' + | ||
node.type + | ||
'. ' + | ||
'Maybe you need to change PostCSS stringifier.' | ||
) | ||
} | ||
/* c8 ignore stop */ | ||
this[node.type](node, semicolon) | ||
} | ||
} | ||
@@ -361,0 +361,0 @@ |
import { Stringifier } from './postcss.js' | ||
declare const stringify: Stringifier | ||
interface Stringify extends Stringifier { | ||
default: Stringify | ||
} | ||
export default stringify | ||
declare const stringify: Stringify | ||
export = stringify |
@@ -14,17 +14,17 @@ 'use strict' | ||
const HIGHLIGHT_THEME = { | ||
'brackets': pico.cyan, | ||
'at-word': pico.cyan, | ||
'comment': pico.gray, | ||
'string': pico.green, | ||
'class': pico.yellow, | ||
'hash': pico.magenta, | ||
'call': pico.cyan, | ||
';': pico.yellow, | ||
':': pico.yellow, | ||
'(': pico.cyan, | ||
')': pico.cyan, | ||
'[': pico.yellow, | ||
']': pico.yellow, | ||
'{': pico.yellow, | ||
'}': pico.yellow, | ||
'[': pico.yellow, | ||
']': pico.yellow, | ||
':': pico.yellow, | ||
';': pico.yellow | ||
'at-word': pico.cyan, | ||
'brackets': pico.cyan, | ||
'call': pico.cyan, | ||
'class': pico.yellow, | ||
'comment': pico.gray, | ||
'hash': pico.magenta, | ||
'string': pico.green | ||
} | ||
@@ -31,0 +31,0 @@ |
@@ -25,3 +25,3 @@ 'use strict' | ||
const RE_WORD_END = /[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g | ||
const RE_BAD_BRACKET = /.[\n"'(/\\]/ | ||
const RE_BAD_BRACKET = /.[\r\n"'(/\\]/ | ||
const RE_HEX_ESCAPE = /[\da-f]/i | ||
@@ -263,6 +263,6 @@ | ||
back, | ||
endOfFile, | ||
nextToken, | ||
endOfFile, | ||
position | ||
} | ||
} |
import { RangePosition } from './css-syntax-error.js' | ||
import Node from './node.js' | ||
export interface WarningOptions { | ||
/** | ||
* CSS node that caused the warning. | ||
*/ | ||
node?: Node | ||
declare namespace Warning { | ||
export interface WarningOptions { | ||
/** | ||
* End position, exclusive, in CSS node string that caused the warning. | ||
*/ | ||
end?: RangePosition | ||
/** | ||
* Word in CSS source that caused the warning. | ||
*/ | ||
word?: string | ||
/** | ||
* End index, exclusive, in CSS node string that caused the warning. | ||
*/ | ||
endIndex?: number | ||
/** | ||
* Start index, inclusive, in CSS node string that caused the warning. | ||
*/ | ||
index?: number | ||
/** | ||
* Start index, inclusive, in CSS node string that caused the warning. | ||
*/ | ||
index?: number | ||
/** | ||
* End index, exclusive, in CSS node string that caused the warning. | ||
*/ | ||
endIndex?: number | ||
/** | ||
* CSS node that caused the warning. | ||
*/ | ||
node?: Node | ||
/** | ||
* Start position, inclusive, in CSS node string that caused the warning. | ||
*/ | ||
start?: RangePosition | ||
/** | ||
* Name of the plugin that created this warning. `Result#warn` fills | ||
* this property automatically. | ||
*/ | ||
plugin?: string | ||
/** | ||
* End position, exclusive, in CSS node string that caused the warning. | ||
*/ | ||
end?: RangePosition | ||
/** | ||
* Start position, inclusive, in CSS node string that caused the warning. | ||
*/ | ||
start?: RangePosition | ||
/** | ||
* Name of the plugin that created this warning. `Result#warn` fills | ||
* this property automatically. | ||
*/ | ||
plugin?: string | ||
/** | ||
* Word in CSS source that caused the warning. | ||
*/ | ||
word?: string | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
export { Warning_ as default } | ||
} | ||
@@ -51,36 +56,29 @@ | ||
*/ | ||
export default class Warning { | ||
declare class Warning_ { | ||
/** | ||
* Type to filter warnings from `Result#messages`. | ||
* Always equal to `"warning"`. | ||
*/ | ||
type: 'warning' | ||
/** | ||
* The warning message. | ||
* Column for inclusive start position in the input file with this warning’s source. | ||
* | ||
* ```js | ||
* warning.text //=> 'Try to avoid !important' | ||
* warning.column //=> 6 | ||
* ``` | ||
*/ | ||
text: string | ||
column: number | ||
/** | ||
* The name of the plugin that created this warning. | ||
* When you call `Node#warn` it will fill this property automatically. | ||
* Column for exclusive end position in the input file with this warning’s source. | ||
* | ||
* ```js | ||
* warning.plugin //=> 'postcss-important' | ||
* warning.endColumn //=> 4 | ||
* ``` | ||
*/ | ||
plugin: string | ||
endColumn?: number | ||
/** | ||
* Contains the CSS node that caused the warning. | ||
* Line for exclusive end position in the input file with this warning’s source. | ||
* | ||
* ```js | ||
* warning.node.toString() //=> 'color: white !important' | ||
* warning.endLine //=> 6 | ||
* ``` | ||
*/ | ||
node: Node | ||
endLine?: number | ||
@@ -97,33 +95,40 @@ /** | ||
/** | ||
* Column for inclusive start position in the input file with this warning’s source. | ||
* Contains the CSS node that caused the warning. | ||
* | ||
* ```js | ||
* warning.column //=> 6 | ||
* warning.node.toString() //=> 'color: white !important' | ||
* ``` | ||
*/ | ||
column: number | ||
node: Node | ||
/** | ||
* Line for exclusive end position in the input file with this warning’s source. | ||
* The name of the plugin that created this warning. | ||
* When you call `Node#warn` it will fill this property automatically. | ||
* | ||
* ```js | ||
* warning.endLine //=> 6 | ||
* warning.plugin //=> 'postcss-important' | ||
* ``` | ||
*/ | ||
endLine?: number | ||
plugin: string | ||
/** | ||
* Column for exclusive end position in the input file with this warning’s source. | ||
* The warning message. | ||
* | ||
* ```js | ||
* warning.endColumn //=> 4 | ||
* warning.text //=> 'Try to avoid !important' | ||
* ``` | ||
*/ | ||
endColumn?: number | ||
text: string | ||
/** | ||
* Type to filter warnings from `Result#messages`. | ||
* Always equal to `"warning"`. | ||
*/ | ||
type: 'warning' | ||
/** | ||
* @param text Warning message. | ||
* @param opts Warning options. | ||
*/ | ||
constructor(text: string, opts?: WarningOptions) | ||
constructor(text: string, opts?: Warning.WarningOptions) | ||
@@ -141,1 +146,5 @@ /** | ||
} | ||
declare class Warning extends Warning_ {} | ||
export = Warning |
@@ -22,4 +22,4 @@ 'use strict' | ||
return this.node.error(this.text, { | ||
index: this.index, | ||
plugin: this.plugin, | ||
index: this.index, | ||
word: this.word | ||
@@ -26,0 +26,0 @@ }).message |
{ | ||
"name": "postcss", | ||
"version": "8.4.17", | ||
"version": "8.4.31", | ||
"description": "Tool for transforming styles with JS plugins", | ||
@@ -11,4 +11,3 @@ "engines": { | ||
"require": "./lib/postcss.js", | ||
"import": "./lib/postcss.mjs", | ||
"types": "./lib/postcss.d.ts" | ||
"import": "./lib/postcss.mjs" | ||
}, | ||
@@ -65,2 +64,6 @@ "./lib/at-rule": "./lib/at-rule.js", | ||
"url": "https://tidelift.com/funding/github/npm/postcss" | ||
}, | ||
{ | ||
"type": "github", | ||
"url": "https://github.com/sponsors/ai" | ||
} | ||
@@ -76,3 +79,3 @@ ], | ||
"dependencies": { | ||
"nanoid": "^3.3.4", | ||
"nanoid": "^3.3.6", | ||
"picocolors": "^1.0.0", | ||
@@ -79,0 +82,0 @@ "source-map-js": "^1.0.2" |
@@ -1,2 +0,2 @@ | ||
# PostCSS [![Gitter][chat-img]][chat] | ||
# PostCSS | ||
@@ -7,5 +7,2 @@ <img align="right" width="95" height="95" | ||
[chat-img]: https://img.shields.io/badge/Gitter-Join_the_PostCSS_chat-brightgreen.svg | ||
[chat]: https://gitter.im/postcss/postcss | ||
PostCSS is a tool for transforming styles with JS plugins. | ||
@@ -16,18 +13,9 @@ These plugins can lint your CSS, support variables and mixins, | ||
PostCSS is used by industry leaders including Wikipedia, Twitter, Alibaba, | ||
and JetBrains. The [Autoprefixer] PostCSS plugin is one of the most popular | ||
CSS processors. | ||
and JetBrains. The [Autoprefixer] and [Stylelint] PostCSS plugins is one of the most popular CSS tools. | ||
PostCSS takes a CSS file and provides an API to analyze and modify its rules | ||
(by transforming them into an [Abstract Syntax Tree]). | ||
This API can then be used by [plugins] to do a lot of useful things, | ||
e.g., to find errors automatically, or to insert vendor prefixes. | ||
--- | ||
**Support / Discussion:** [Gitter](https://gitter.im/postcss/postcss)<br> | ||
**Twitter account:** [@postcss](https://twitter.com/postcss)<br> | ||
**VK.com page:** [postcss](https://vk.com/postcss)<br> | ||
**中文翻译**: [`docs/README-cn.md`](./docs/README-cn.md) | ||
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" /> Made in <b><a href="https://evilmartians.com/?utm_source=postcss&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>. | ||
For PostCSS commercial support (consulting, improving the front-end culture | ||
of your company, PostCSS plugins), contact [Evil Martians] | ||
at <postcss@evilmartians.com>. | ||
--- | ||
@@ -37,11 +25,7 @@ [Abstract Syntax Tree]: https://en.wikipedia.org/wiki/Abstract_syntax_tree | ||
[Autoprefixer]: https://github.com/postcss/autoprefixer | ||
[Stylelint]: https://stylelint.io/ | ||
[plugins]: https://github.com/postcss/postcss#plugins | ||
<a href="https://evilmartians.com/?utm_source=postcss"> | ||
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" | ||
alt="Sponsored by Evil Martians" width="236" height="54"> | ||
</a> | ||
## Docs | ||
Read **[full docs](https://github.com/postcss/postcss#readme)** on GitHub. | ||
Read full docs **[here](https://postcss.org/)**. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
196517
55
6745
29
3
Updatednanoid@^3.3.6