Skip to content

postcss-nesting : typescript and docs #613

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion plugins/postcss-nesting/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changes to PostCSS Nesting

### Unreleased

- Added: TypeScript typings

### 10.1.10 (June 30, 2022)

- Partially revert the changes to pseudo element selectors from 10.1.9.
Expand All @@ -13,7 +17,7 @@

/* becomes */

- .something_else > :is(.anything:::before) { /* 10.1.9 */
- .something_else > :is(.anything::before) { /* 10.1.9 */
+ .something_else > .anything::before { /* previous and restored behavior */
order: 1;
}
Expand Down
36 changes: 19 additions & 17 deletions plugins/postcss-nesting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,31 @@ you might want to use [PostCSS Nested] instead.

```pcss
a, b {
color: red;
color: red;

& c, & d {
color: white;
}
/* "&" comes first */
& c, & d {
color: white;
}

/* "&" comes later, requiring "@nest" */
@nest e & {
color: yellow;
}
}

/* becomes */

a, b {
color: red;
color: red;
}

a c, a d, b c, b d {
color: white;
color: white;
}

e a, e b {
color: yellow;
}
```

Expand All @@ -36,15 +46,7 @@ Add [PostCSS Nesting] to your project:
npm install postcss-nesting --save-dev
```

Use [PostCSS Nesting] to process your CSS:

```js
import postcssNesting from 'postcss-nesting';

postcssNesting.process(YOUR_CSS /*, processOptions, pluginOptions */);
```

Or use it as a [PostCSS] plugin:
Use [PostCSS Nesting] as a [PostCSS] plugin:

```js
import postcss from 'postcss';
Expand Down Expand Up @@ -174,8 +176,8 @@ _writing the selector without nesting is advised here_

### ⚠️ Spec disclaimer

The [CSS Nesting Module] spec states on nesting that "Declarations occuring after a nested rule are invalid and ignored.".
While we think it makes sense on browsers, enforcing this at the plugin level introduces several constrains that would
The [CSS Nesting Module] spec states on nesting that "Declarations occurring after a nested rule are invalid and ignored.".
While we think it makes sense on browsers, enforcing this at the plugin level introduces several constraints that would
interfere with PostCSS' plugin nature such as with `@mixin`

[css-img]: https://cssdb.org/images/badges/nesting-rules.svg
Expand Down
2 changes: 1 addition & 1 deletion plugins/postcss-nesting/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"jsdelivr": "dist/index.mjs",
"unpkg": "dist/index.mjs",
"exports": {
Expand Down Expand Up @@ -41,7 +42,6 @@
"lint:eslint": "eslint ./src --ext .js --ext .ts --ext .mjs --no-error-on-unmatched-pattern",
"lint:package-json": "node ../../.github/bin/format-package-json.mjs",
"prepublishOnly": "npm run clean && npm run build && npm run test",
"stryker": "stryker run --logLevel error",
"test": "node .tape.mjs && npm run test:exports",
"test:deno": "deno run --unstable --allow-env --allow-read test/deno/test.js",
"test:exports": "node ./test/_import.mjs && node ./test/_require.cjs",
Expand Down
17 changes: 0 additions & 17 deletions plugins/postcss-nesting/src/index.js

This file was deleted.

26 changes: 26 additions & 0 deletions plugins/postcss-nesting/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { PluginCreator } from 'postcss';
import walk from './lib/walk.js';

type pluginOptions = { noIsPseudoSelector?: boolean };

const creator: PluginCreator<pluginOptions> = (opts?: pluginOptions) => {
const options = Object.assign(
// Default options
{
noIsPseudoSelector: false,
},
// Provided options
opts,
);

return {
postcssPlugin: 'postcss-nesting',
Rule(rule) {
walk(rule, options);
},
};
};

creator.postcss = true;

export default creator;
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import type { AtRule } from 'postcss';
import cleanupParent from './cleanup-parent.js';
import mergeParams from './merge-params.js';
import shiftNodesBeforeParent from './shift-nodes-before-parent.js';
import validAtrules from './valid-atrules.js';

/*
* DEPRECATED: In v7.0.0 these features will be removed as they are not part of
* the nesting proposal.
*/

export default function transformAtruleWithinAtrule(node) {
export default function transformAtruleWithinAtrule(node: AtRule, parent: AtRule) {
// move previous siblings and the node to before the parent
const parent = shiftNodesBeforeParent(node);
shiftNodesBeforeParent(node, parent);

// update the params of the node to be merged with the parent
node.params = mergeParams(parent.params, node.params);
Expand All @@ -19,4 +15,7 @@ export default function transformAtruleWithinAtrule(node) {
cleanupParent(parent);
}

export const isAtruleWithinAtrule = (node) => node.type === 'atrule' && validAtrules.includes(node.name) && Object(node.parent).type === 'atrule' && node.name === node.parent.name;
export function isAtruleWithinAtrule(node: AtRule, parent: AtRule) {
return validAtrules.includes(node.name) &&
node.name === parent.name;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import cleanupParent from './cleanup-parent.js';
import { options } from './options.js';
import shiftNodesBeforeParent from './shift-nodes-before-parent.js';
import validAtrules from './valid-atrules.js';
import { walkFunc } from './walk-func.js';
import type { AtRule, Rule } from 'postcss';

export default function atruleWithinRule(node, walk, opts) {
export default function atruleWithinRule(node: AtRule, parent: Rule, walk: walkFunc, opts: options) {
// move previous siblings and the node to before the parent
const parent = shiftNodesBeforeParent(node);
shiftNodesBeforeParent(node, parent);

// clone the parent as a new rule with children appended to it
const rule = parent.clone().removeAll().append(node.nodes);
Expand All @@ -19,4 +22,6 @@ export default function atruleWithinRule(node, walk, opts) {
walk(rule, opts);
}

export const isAtruleWithinRule = (node) => node.type === 'atrule' && validAtrules.includes(node.name) && Object(node.parent).type === 'rule';
export function isAtruleWithinRule(node: AtRule) {
return validAtrules.includes(node.name);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default function cleanupParent(parent) {
import type { ChildNode, Container } from 'postcss';

export default function cleanupParent(parent: Container<ChildNode>) {
if (!parent.nodes.length) {
parent.remove();
return;
Expand Down
13 changes: 13 additions & 0 deletions plugins/postcss-nesting/src/lib/is-type-of-rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { AtRule, Node, Rule } from 'postcss';

export function isAtRule(node?: Node): node is AtRule {
return node && node.type === 'atrule';
}

export function isNestRule(node?: Node): node is AtRule {
return node && isAtRule(node) && node.name === 'nest';
}

export function isRule(node?: Node): node is Rule {
return node && node.type === 'rule';
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export const comma = (string) => {
let array = [];
export function comma(string: string) {
const array: Array<string> = [];
let current = '';
let split = false;

let func = 0;
let quote = false;
let quote: string|false = false;
let escape = false;

for (let letter of string) {
for (const letter of string) {
if (escape) {
escape = false;
} else if (letter === '\\') {
Expand Down Expand Up @@ -43,4 +43,4 @@ export const comma = (string) => {

array.push(current.trim());
return array;
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { comma } from './list.js';

export default function mergeParams(fromParams, toParams) {
export default function mergeParams(fromParams: string, toParams: string) {
return comma(fromParams)
.map((params1) =>
comma(toParams)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function combinationsWithSizeN(set, n) {
export function combinationsWithSizeN(set: Array<string>, n: number): Array<Array<string>> {
// set is the list of parent selectors
// n is the amount of `&` selectors in the current selector.
// all combinations of values in the set with an array size of n must be generated to match the nesting selector behavior.
Expand Down Expand Up @@ -37,17 +37,17 @@ export function combinationsWithSizeN(set, n) {
throw new Error('Too many combinations when trying to resolve a nested selector with lists, reduce the complexity of your selectors');
}

const counters = [];
const counters: Array<number> = [];

for (let i = 0; i < n; i++) {
counters[i] = 0;
}

const result = [];
const result: Array<Array<string>> = [];

// eslint-disable-next-line no-constant-condition
while (true) {
const ss = [];
const ss : Array<string> = [];
for (let i = n-1; i >=0; i--) {
let currentCounter = counters[i];
if (currentCounter >= set.length) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import parser from 'postcss-selector-parser';
import type { Container, Node, Pseudo } from 'postcss-selector-parser';

const isPseudo = parser.pseudo({ value: ':is' });

export function sortCompoundSelectorsInsideComplexSelector(node) {
export function sortCompoundSelectorsInsideComplexSelector(node: Container<string, Node>) {
if (!node || !node.nodes) {
return;
}

const compoundSelectors = [];
let currentCompoundSelector = [];
const compoundSelectors: Array<Array<Node>> = [];
let currentCompoundSelector: Array<Node> = [];
for (let i = 0; i < node.nodes.length; i++) {
if (node.nodes[i].type === 'combinator') {
// Push the current compound selector
Expand All @@ -34,10 +35,13 @@ export function sortCompoundSelectorsInsideComplexSelector(node) {
}

if (node.nodes[i].type === 'tag' && currentCompoundSelector.find(x => x.type === 'tag')) {
const isPseudoClone = isPseudo.clone();
const isPseudoClone = isPseudo.clone({}) as Pseudo;
const child = node.nodes[i];
child.replaceWith(isPseudoClone);
isPseudoClone.append(child);
isPseudoClone.append(parser.selector({
nodes: [child],
value: undefined,
}));
}

currentCompoundSelector.push(node.nodes[i]);
Expand All @@ -50,18 +54,18 @@ export function sortCompoundSelectorsInsideComplexSelector(node) {
const compoundSelector = compoundSelectors[i];
compoundSelector.sort((a, b) => {
if (a.type === 'selector' && b.type === 'selector' && a.nodes.length && b.nodes.length) {
return selectorTypeOrder(a.nodes[0], a.nodes[0].type) - selectorTypeOrder(b.nodes[0], b.nodes[0].type);
return selectorTypeOrder(a.nodes[0]) - selectorTypeOrder(b.nodes[0]);
}

if (a.type === 'selector' && a.nodes.length) {
return selectorTypeOrder(a.nodes[0], a.nodes[0].type) - selectorTypeOrder(b, b.type);
return selectorTypeOrder(a.nodes[0]) - selectorTypeOrder(b);
}

if (b.type === 'selector' && b.nodes.length) {
return selectorTypeOrder(a, a.type) - selectorTypeOrder(b.nodes[0], b.nodes[0].type);
return selectorTypeOrder(a) - selectorTypeOrder(b.nodes[0]);
}

return selectorTypeOrder(a, a.type) - selectorTypeOrder(b, b.type);
return selectorTypeOrder(a) - selectorTypeOrder(b);
});

for (let j = 0; j < compoundSelector.length; j++) {
Expand All @@ -75,12 +79,12 @@ export function sortCompoundSelectorsInsideComplexSelector(node) {
}
}

function selectorTypeOrder(selector, type) {
function selectorTypeOrder(selector: Node) {
if (parser.isPseudoElement(selector)) {
return selectorTypeOrderIndex.pseudoElement;
}

return selectorTypeOrderIndex[type];
return selectorTypeOrderIndex[selector.type];
}

const selectorTypeOrderIndex = {
Expand Down
Loading