Project files

This commit is contained in:
2023-11-09 18:47:11 +01:00
parent 695abe054b
commit c415135aae
8554 changed files with 858111 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
# Changes to PostCSS Custom Selectors
### 6.0.0 (January 12, 2021)
- Added: Support for PostCSS v8
- Added: Support for Node v10+
- Fixed: importing from multiple sources ([#42](https://github.com/postcss/postcss-custom-selectors/pull/42))
### 5.1.2 (September 20, 2018)
- Fixed: Do not break on an empty `importFrom` object
### 5.1.1 (September 18, 2018)
- Fixed: Selectors like `.foo:--h1` become `h1.foo` instead of `.fooh1`
### 5.1.0 (September 12, 2018)
- Added: New `exportTo` function to specify where to export custom selectors
- Updated: `importFrom` option to support passing it a function
### 5.0.0 (September 7, 2018)
- Added: New `preserve` option to preserve custom selectors and rules using them
- Added: New `importFrom` option to specify where to import custom selectors
- Added: Support for PostCSS v7
- Added: Support for Node v6+
### 4.0.1 (May 15, 2017)
- Fixed: incorrect export
### 4.0.0 (May 12, 2017)
- Added: compatibility with postcss v6.x
### 3.0.0 (August 25, 2015)
- Removed: compatibility with postcss v4.x
- Added: compatibility with postcss v5.x
### 2.3.0 (July 14, 2015)
* Fixed: Nested/mixed selectors now works correctly
([#19](https://github.com/postcss/postcss-custom-selectors/issues/19))
* Added: `transformMatches` option to limit transformation to :matches()
replacements.
### 2.2.0 (June 30, 2015)
* Fixed: No more useless warnings for undefined non custom selectors
([#22](https://github.com/postcss/postcss-custom-selectors/issues/22))
* Changed: warnings now use PostCSS message API
### 2.1.1 (June 30, 2015)
* Fixed: the lineBreak option keeping the selectors indent
([#18](https://github.com/postcss/postcss-custom-selectors/issues/18))
* Fixed: the tip of an undefined selector
([#20](https://github.com/postcss/postcss-custom-selectors/issues/20))
### 2.1.0 (June 4, 2015)
* Changed: use PostCSS 4.1 plugin API
([#13](https://github.com/postcss/postcss-custom-selectors/issues/13))
### 2.0.1 (June 3, 2015)
* Fixed: `(foo, bar)` conversion error exists in the selector
(See also [:matches() test](test/fixtures/matches/input.css))
### 2.0.0 (May 29, 2015)
* Removed: no longer support `::` or `--` to defined a custom selectors,
you must use the syntax `:--` to define it.
([#6](https://github.com/postcss/postcss-custom-selectors/issues/6))
* Fixed: two or more consecutive hyphens in selector don't output `undefined`
([#14](https://github.com/postcss/postcss-custom-selectors/issues/14))
### 1.1.1 (April 6, 2015)
* Fixed: add support for multilines definition
### 1.1.0 (December 6, 2014)
* Added: "lineBreak" option
### 1.0.0 (December 6, 2014)
* First release

View File

@@ -0,0 +1,21 @@
# The MIT License (MIT)
Copyright © PostCSS
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,166 @@
# PostCSS Custom Selectors [<img src="https://postcss.github.io/postcss/logo.svg" alt="PostCSS" width="90" height="90" align="right">][postcss]
[![NPM Version][npm-img]][npm-url]
[![CSS Standard Status][css-img]][css-url]
[![Build Status][cli-img]][cli-url]
[![Support Chat][git-img]][git-url]
[PostCSS Custom Selectors] lets you use Custom Selectors in CSS, following the
[CSS Extensions] specification.
```pcss
@custom-selector :--heading h1, h2, h3;
article :--heading + p {
margin-top: 0;
}
/* becomes */
article h1 + p, article h2 + p, article h3 + p {}
```
## Usage
Add [PostCSS Custom Selectors] to your project:
```bash
npm install postcss-custom-selectors --save-dev
```
Use [PostCSS Custom Selectors] to process your CSS:
```js
const postcssCustomSelectors = require('postcss-custom-selectors');
postcssCustomSelectors.process(YOUR_CSS /*, processOptions, pluginOptions */);
```
Or use it as a [PostCSS] plugin:
```js
const postcss = require('postcss');
const postcssCustomSelectors = require('postcss-custom-selectors');
postcss([
postcssCustomSelectors(/* pluginOptions */)
]).process(YOUR_CSS /*, processOptions */);
```
[PostCSS Custom Selectors] runs in all Node environments, with special instructions for:
| [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) |
| --- | --- | --- | --- | --- | --- |
## Options
### preserve
The `preserve` option determines whether custom selectors and rules using
custom selectors should be preserved in their original form.
```pcss
@custom-selector :--heading h1, h2, h3;
article :--heading + p {
margin-top: 0;
}
/* becomes */
article h1 + p, article h2 + p, article h3 + p {}
article :--heading + p {}
```
### importFrom
The `importFrom` option specifies sources where custom selectors can be
imported from, which might be CSS, JS, and JSON files, functions, and directly
passed objects.
```js
postcssCustomSelectors({
importFrom: 'path/to/file.css' // => @custom-selector :--heading h1, h2, h3;
});
```
```pcss
article :--heading + p {
margin-top: 0;
}
/* becomes */
article h1 + p, article h2 + p, article h3 + p {}
```
Multiple sources can be passed into this option, and they will be parsed in the
order they are received. JavaScript files, JSON files, functions, and objects
will need to namespace custom selectors using the `customProperties` or
`custom-properties` key.
```js
postcssCustomSelectors({
importFrom: [
'path/to/file.css',
'and/then/this.js',
'and/then/that.json',
{
customSelectors: { ':--heading': 'h1, h2, h3' }
},
() => {
const customProperties = { ':--heading': 'h1, h2, h3' };
return { customProperties };
}
]
});
```
### exportTo
The `exportTo` option specifies destinations where custom selectors can be
exported to, which might be CSS, JS, and JSON files, functions, and directly
passed objects.
```js
postcssCustomSelectors({
exportTo: 'path/to/file.css' // @custom-selector :--heading h1, h2, h3;
});
```
Multiple destinations can be passed into this option, and they will be parsed
in the order they are received. JavaScript files, JSON files, and objects will
need to namespace custom selectors using the `customProperties` or
`custom-properties` key.
```js
const cachedObject = { customSelectors: {} };
postcssCustomSelectors({
exportTo: [
'path/to/file.css', // @custom-selector :--heading h1, h2, h3;
'and/then/this.js', // module.exports = { customSelectors: { ':--heading': 'h1, h2, h3' } }
'and/then/this.mjs', // export const customSelectors = { ':--heading': 'h1, h2, h3' } }
'and/then/that.json', // { "custom-selectors": { ":--heading": "h1, h2, h3" } }
cachedObject,
customProperties => {
customProperties // { ':--heading': 'h1, h2, h3' }
}
]
});
```
[cli-img]: https://img.shields.io/travis/postcss/postcss-custom-selectors.svg
[cli-url]: https://travis-ci.org/postcss/postcss-custom-selectors
[css-img]: https://cssdb.org/badge/custom-selectors.svg
[css-url]: https://cssdb.org/#custom-selectors
[git-img]: https://img.shields.io/badge/support-chat-blue.svg
[git-url]: https://gitter.im/postcss/postcss
[npm-img]: https://img.shields.io/npm/v/postcss-custom-selectors.svg
[npm-url]: https://www.npmjs.com/package/postcss-custom-selectors
[CSS Extensions]: https://drafts.csswg.org/css-extensions/#custom-selectors
[PostCSS]: https://github.com/postcss/postcss
[PostCSS Custom Selectors]: https://github.com/postcss/postcss-custom-selectors

View File

@@ -0,0 +1,434 @@
'use strict';
var parser = require('postcss-selector-parser');
var fs = require('fs');
var path = require('path');
var postcss = require('postcss');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
}
});
}
n['default'] = e;
return Object.freeze(n);
}
var parser__default = /*#__PURE__*/_interopDefaultLegacy(parser);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var postcss__default = /*#__PURE__*/_interopDefaultLegacy(postcss);
/* Return a Selectors AST from a Selectors String
/* ========================================================================== */
var getSelectorsAstFromSelectorsString = (selectorString => {
let selectorAST;
parser__default['default'](selectors => {
selectorAST = selectors;
}).processSync(selectorString);
return selectorAST;
});
var getCustomSelectors = ((root, opts) => {
// initialize custom selectors
const customSelectors = {}; // for each custom selector atrule that is a child of the css root
root.nodes.slice().forEach(node => {
if (isCustomSelector(node)) {
// extract the name and selectors from the params of the custom selector
const [, name, selectors] = node.params.match(customSelectorParamsRegExp); // write the parsed selectors to the custom selector
customSelectors[name] = getSelectorsAstFromSelectorsString(selectors); // conditionally remove the custom selector atrule
if (!Object(opts).preserve) {
node.remove();
}
}
});
return customSelectors;
}); // match the custom selector name
const customSelectorNameRegExp = /^custom-selector$/i; // match the custom selector params
const customSelectorParamsRegExp = /^(:--[A-z][\w-]*)\s+([\W\w]+)\s*$/; // whether the atrule is a custom selector
const isCustomSelector = node => node.type === 'atrule' && customSelectorNameRegExp.test(node.name) && customSelectorParamsRegExp.test(node.params);
// return transformed selectors, replacing custom pseudo selectors with custom selectors
function transformSelectorList(selectorList, customSelectors) {
let index = selectorList.nodes.length - 1;
while (index >= 0) {
const transformedSelectors = transformSelector(selectorList.nodes[index], customSelectors);
if (transformedSelectors.length) {
selectorList.nodes.splice(index, 1, ...transformedSelectors);
}
--index;
}
return selectorList;
} // return custom pseudo selectors replaced with custom selectors
function transformSelector(selector, customSelectors) {
const transpiledSelectors = [];
for (const index in selector.nodes) {
const {
value,
nodes
} = selector.nodes[index];
if (value in customSelectors) {
for (const replacementSelector of customSelectors[value].nodes) {
const selectorClone = selector.clone();
selectorClone.nodes.splice(index, 1, ...replacementSelector.clone().nodes.map(node => {
// use spacing from the current usage
node.spaces = { ...selector.nodes[index].spaces
};
return node;
}));
const retranspiledSelectors = transformSelector(selectorClone, customSelectors);
adjustNodesBySelectorEnds(selectorClone.nodes, Number(index));
if (retranspiledSelectors.length) {
transpiledSelectors.push(...retranspiledSelectors);
} else {
transpiledSelectors.push(selectorClone);
}
}
return transpiledSelectors;
} else if (nodes && nodes.length) {
transformSelectorList(selector.nodes[index], customSelectors);
}
}
return transpiledSelectors;
} // match selectors by difficult-to-separate ends
const withoutSelectorStartMatch = /^(tag|universal)$/;
const withoutSelectorEndMatch = /^(class|id|pseudo|tag|universal)$/;
const isWithoutSelectorStart = node => withoutSelectorStartMatch.test(Object(node).type);
const isWithoutSelectorEnd = node => withoutSelectorEndMatch.test(Object(node).type); // adjust nodes by selector ends (so that .class:--h1 becomes h1.class rather than .classh1)
const adjustNodesBySelectorEnds = (nodes, index) => {
if (index && isWithoutSelectorStart(nodes[index]) && isWithoutSelectorEnd(nodes[index - 1])) {
let safeIndex = index - 1;
while (safeIndex && isWithoutSelectorEnd(nodes[safeIndex])) {
--safeIndex;
}
if (safeIndex < index) {
const node = nodes.splice(index, 1)[0];
nodes.splice(safeIndex, 0, node);
nodes[safeIndex].spaces.before = nodes[safeIndex + 1].spaces.before;
nodes[safeIndex + 1].spaces.before = '';
if (nodes[index]) {
nodes[index].spaces.after = nodes[safeIndex].spaces.after;
nodes[safeIndex].spaces.after = '';
}
}
}
};
var transformRules = ((root, customSelectors, opts) => {
root.walkRules(customPseudoRegExp, rule => {
const selector = parser__default['default'](selectors => {
transformSelectorList(selectors, customSelectors);
}).processSync(rule.selector);
if (opts.preserve) {
rule.cloneBefore({
selector
});
} else {
rule.selector = selector;
}
});
});
const customPseudoRegExp = /:--[A-z][\w-]*/;
/* Import Custom Selectors from CSS AST
/* ========================================================================== */
function importCustomSelectorsFromCSSAST(root) {
return getCustomSelectors(root);
}
/* Import Custom Selectors from CSS File
/* ========================================================================== */
async function importCustomSelectorsFromCSSFile(from) {
const css = await readFile(path__default['default'].resolve(from));
const root = postcss__default['default'].parse(css, {
from: path__default['default'].resolve(from)
});
return importCustomSelectorsFromCSSAST(root);
}
/* Import Custom Selectors from Object
/* ========================================================================== */
function importCustomSelectorsFromObject(object) {
const customSelectors = Object.assign({}, Object(object).customSelectors || Object(object)['custom-selectors']);
for (const key in customSelectors) {
customSelectors[key] = getSelectorsAstFromSelectorsString(customSelectors[key]);
}
return customSelectors;
}
/* Import Custom Selectors from JSON file
/* ========================================================================== */
async function importCustomSelectorsFromJSONFile(from) {
const object = await readJSON(path__default['default'].resolve(from));
return importCustomSelectorsFromObject(object);
}
/* Import Custom Selectors from JS file
/* ========================================================================== */
async function importCustomSelectorsFromJSFile(from) {
const object = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(path__default['default'].resolve(from))); });
return importCustomSelectorsFromObject(object);
}
/* Import Custom Selectors from Sources
/* ========================================================================== */
function importCustomSelectorsFromSources(sources) {
return sources.map(source => {
if (source instanceof Promise) {
return source;
} else if (source instanceof Function) {
return source();
} // read the source as an object
const opts = source === Object(source) ? source : {
from: String(source)
}; // skip objects with custom selectors
if (Object(opts).customSelectors || Object(opts)['custom-selectors']) {
return opts;
} // source pathname
const from = String(opts.from || ''); // type of file being read from
const type = (opts.type || path__default['default'].extname(from).slice(1)).toLowerCase();
return {
type,
from
};
}).reduce(async (customSelectorsPromise, source) => {
const customSelectors = await customSelectorsPromise;
const {
type,
from
} = await source;
if (type === 'ast') {
return Object.assign(customSelectors, importCustomSelectorsFromCSSAST(from));
}
if (type === 'css') {
return Object.assign(customSelectors, await importCustomSelectorsFromCSSFile(from));
}
if (type === 'js') {
return Object.assign(customSelectors, await importCustomSelectorsFromJSFile(from));
}
if (type === 'json') {
return Object.assign(customSelectors, await importCustomSelectorsFromJSONFile(from));
}
return Object.assign(customSelectors, importCustomSelectorsFromObject(await source));
}, Promise.resolve({}));
}
/* Helper utilities
/* ========================================================================== */
const readFile = from => new Promise((resolve, reject) => {
fs__default['default'].readFile(from, 'utf8', (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
const readJSON = async from => JSON.parse(await readFile(from));
/* Import Custom Selectors from CSS File
/* ========================================================================== */
async function exportCustomSelectorsToCssFile(to, customSelectors) {
const cssContent = Object.keys(customSelectors).reduce((cssLines, name) => {
cssLines.push(`@custom-selector ${name} ${customSelectors[name]};`);
return cssLines;
}, []).join('\n');
const css = `${cssContent}\n`;
await writeFile(to, css);
}
/* Import Custom Selectors from JSON file
/* ========================================================================== */
async function exportCustomSelectorsToJsonFile(to, customSelectors) {
const jsonContent = JSON.stringify({
'custom-selectors': customSelectors
}, null, ' ');
const json = `${jsonContent}\n`;
await writeFile(to, json);
}
/* Import Custom Selectors from Common JS file
/* ========================================================================== */
async function exportCustomSelectorsToCjsFile(to, customSelectors) {
const jsContents = Object.keys(customSelectors).reduce((jsLines, name) => {
jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(customSelectors[name])}'`);
return jsLines;
}, []).join(',\n');
const js = `module.exports = {\n\tcustomSelectors: {\n${jsContents}\n\t}\n};\n`;
await writeFile(to, js);
}
/* Import Custom Selectors from Module JS file
/* ========================================================================== */
async function exportCustomSelectorsToMjsFile(to, customSelectors) {
const mjsContents = Object.keys(customSelectors).reduce((mjsLines, name) => {
mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(customSelectors[name])}'`);
return mjsLines;
}, []).join(',\n');
const mjs = `export const customSelectors = {\n${mjsContents}\n};\n`;
await writeFile(to, mjs);
}
/* Export Custom Selectors to Destinations
/* ========================================================================== */
function exportCustomSelectorsToDestinations(customSelectors, destinations) {
return Promise.all(destinations.map(async destination => {
if (destination instanceof Function) {
await destination(defaultCustomSelectorsToJSON(customSelectors));
} else {
// read the destination as an object
const opts = destination === Object(destination) ? destination : {
to: String(destination)
}; // transformer for custom selectors into a JSON-compatible object
const toJSON = opts.toJSON || defaultCustomSelectorsToJSON;
if ('customSelectors' in opts) {
// write directly to an object as customSelectors
opts.customSelectors = toJSON(customSelectors);
} else if ('custom-selectors' in opts) {
// write directly to an object as custom-selectors
opts['custom-selectors'] = toJSON(customSelectors);
} else {
// destination pathname
const to = String(opts.to || ''); // type of file being written to
const type = (opts.type || path__default['default'].extname(opts.to).slice(1)).toLowerCase(); // transformed custom selectors
const customSelectorsJSON = toJSON(customSelectors);
if (type === 'css') {
await exportCustomSelectorsToCssFile(to, customSelectorsJSON);
}
if (type === 'js') {
await exportCustomSelectorsToCjsFile(to, customSelectorsJSON);
}
if (type === 'json') {
await exportCustomSelectorsToJsonFile(to, customSelectorsJSON);
}
if (type === 'mjs') {
await exportCustomSelectorsToMjsFile(to, customSelectorsJSON);
}
}
}
}));
}
/* Helper utilities
/* ========================================================================== */
const defaultCustomSelectorsToJSON = customSelectors => {
return Object.keys(customSelectors).reduce((customSelectorsJSON, key) => {
customSelectorsJSON[key] = String(customSelectors[key]);
return customSelectorsJSON;
}, {});
};
const writeFile = (to, text) => new Promise((resolve, reject) => {
fs__default['default'].writeFile(to, text, error => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
const escapeForJS = string => string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
const postcssCustomSelectors = opts => {
// whether to preserve custom selectors and rules using them
const preserve = Boolean(Object(opts).preserve); // sources to import custom selectors from
const importFrom = [].concat(Object(opts).importFrom || []); // destinations to export custom selectors to
const exportTo = [].concat(Object(opts).exportTo || []); // promise any custom selectors are imported
const customSelectorsPromise = importCustomSelectorsFromSources(importFrom);
return {
postcssPlugin: 'postcss-custom-selectors',
async Once(root) {
const customProperties = Object.assign({}, await customSelectorsPromise, getCustomSelectors(root, {
preserve
}));
await exportCustomSelectorsToDestinations(customProperties, exportTo);
transformRules(root, customProperties, {
preserve
});
}
};
};
postcssCustomSelectors.postcss = true;
module.exports = postcssCustomSelectors;
//# sourceMappingURL=index.cjs.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,405 @@
import parser from 'postcss-selector-parser';
import fs from 'fs';
import path from 'path';
import postcss from 'postcss';
/* Return a Selectors AST from a Selectors String
/* ========================================================================== */
var getSelectorsAstFromSelectorsString = (selectorString => {
let selectorAST;
parser(selectors => {
selectorAST = selectors;
}).processSync(selectorString);
return selectorAST;
});
var getCustomSelectors = ((root, opts) => {
// initialize custom selectors
const customSelectors = {}; // for each custom selector atrule that is a child of the css root
root.nodes.slice().forEach(node => {
if (isCustomSelector(node)) {
// extract the name and selectors from the params of the custom selector
const [, name, selectors] = node.params.match(customSelectorParamsRegExp); // write the parsed selectors to the custom selector
customSelectors[name] = getSelectorsAstFromSelectorsString(selectors); // conditionally remove the custom selector atrule
if (!Object(opts).preserve) {
node.remove();
}
}
});
return customSelectors;
}); // match the custom selector name
const customSelectorNameRegExp = /^custom-selector$/i; // match the custom selector params
const customSelectorParamsRegExp = /^(:--[A-z][\w-]*)\s+([\W\w]+)\s*$/; // whether the atrule is a custom selector
const isCustomSelector = node => node.type === 'atrule' && customSelectorNameRegExp.test(node.name) && customSelectorParamsRegExp.test(node.params);
// return transformed selectors, replacing custom pseudo selectors with custom selectors
function transformSelectorList(selectorList, customSelectors) {
let index = selectorList.nodes.length - 1;
while (index >= 0) {
const transformedSelectors = transformSelector(selectorList.nodes[index], customSelectors);
if (transformedSelectors.length) {
selectorList.nodes.splice(index, 1, ...transformedSelectors);
}
--index;
}
return selectorList;
} // return custom pseudo selectors replaced with custom selectors
function transformSelector(selector, customSelectors) {
const transpiledSelectors = [];
for (const index in selector.nodes) {
const {
value,
nodes
} = selector.nodes[index];
if (value in customSelectors) {
for (const replacementSelector of customSelectors[value].nodes) {
const selectorClone = selector.clone();
selectorClone.nodes.splice(index, 1, ...replacementSelector.clone().nodes.map(node => {
// use spacing from the current usage
node.spaces = { ...selector.nodes[index].spaces
};
return node;
}));
const retranspiledSelectors = transformSelector(selectorClone, customSelectors);
adjustNodesBySelectorEnds(selectorClone.nodes, Number(index));
if (retranspiledSelectors.length) {
transpiledSelectors.push(...retranspiledSelectors);
} else {
transpiledSelectors.push(selectorClone);
}
}
return transpiledSelectors;
} else if (nodes && nodes.length) {
transformSelectorList(selector.nodes[index], customSelectors);
}
}
return transpiledSelectors;
} // match selectors by difficult-to-separate ends
const withoutSelectorStartMatch = /^(tag|universal)$/;
const withoutSelectorEndMatch = /^(class|id|pseudo|tag|universal)$/;
const isWithoutSelectorStart = node => withoutSelectorStartMatch.test(Object(node).type);
const isWithoutSelectorEnd = node => withoutSelectorEndMatch.test(Object(node).type); // adjust nodes by selector ends (so that .class:--h1 becomes h1.class rather than .classh1)
const adjustNodesBySelectorEnds = (nodes, index) => {
if (index && isWithoutSelectorStart(nodes[index]) && isWithoutSelectorEnd(nodes[index - 1])) {
let safeIndex = index - 1;
while (safeIndex && isWithoutSelectorEnd(nodes[safeIndex])) {
--safeIndex;
}
if (safeIndex < index) {
const node = nodes.splice(index, 1)[0];
nodes.splice(safeIndex, 0, node);
nodes[safeIndex].spaces.before = nodes[safeIndex + 1].spaces.before;
nodes[safeIndex + 1].spaces.before = '';
if (nodes[index]) {
nodes[index].spaces.after = nodes[safeIndex].spaces.after;
nodes[safeIndex].spaces.after = '';
}
}
}
};
var transformRules = ((root, customSelectors, opts) => {
root.walkRules(customPseudoRegExp, rule => {
const selector = parser(selectors => {
transformSelectorList(selectors, customSelectors);
}).processSync(rule.selector);
if (opts.preserve) {
rule.cloneBefore({
selector
});
} else {
rule.selector = selector;
}
});
});
const customPseudoRegExp = /:--[A-z][\w-]*/;
/* Import Custom Selectors from CSS AST
/* ========================================================================== */
function importCustomSelectorsFromCSSAST(root) {
return getCustomSelectors(root);
}
/* Import Custom Selectors from CSS File
/* ========================================================================== */
async function importCustomSelectorsFromCSSFile(from) {
const css = await readFile(path.resolve(from));
const root = postcss.parse(css, {
from: path.resolve(from)
});
return importCustomSelectorsFromCSSAST(root);
}
/* Import Custom Selectors from Object
/* ========================================================================== */
function importCustomSelectorsFromObject(object) {
const customSelectors = Object.assign({}, Object(object).customSelectors || Object(object)['custom-selectors']);
for (const key in customSelectors) {
customSelectors[key] = getSelectorsAstFromSelectorsString(customSelectors[key]);
}
return customSelectors;
}
/* Import Custom Selectors from JSON file
/* ========================================================================== */
async function importCustomSelectorsFromJSONFile(from) {
const object = await readJSON(path.resolve(from));
return importCustomSelectorsFromObject(object);
}
/* Import Custom Selectors from JS file
/* ========================================================================== */
async function importCustomSelectorsFromJSFile(from) {
const object = await import(path.resolve(from));
return importCustomSelectorsFromObject(object);
}
/* Import Custom Selectors from Sources
/* ========================================================================== */
function importCustomSelectorsFromSources(sources) {
return sources.map(source => {
if (source instanceof Promise) {
return source;
} else if (source instanceof Function) {
return source();
} // read the source as an object
const opts = source === Object(source) ? source : {
from: String(source)
}; // skip objects with custom selectors
if (Object(opts).customSelectors || Object(opts)['custom-selectors']) {
return opts;
} // source pathname
const from = String(opts.from || ''); // type of file being read from
const type = (opts.type || path.extname(from).slice(1)).toLowerCase();
return {
type,
from
};
}).reduce(async (customSelectorsPromise, source) => {
const customSelectors = await customSelectorsPromise;
const {
type,
from
} = await source;
if (type === 'ast') {
return Object.assign(customSelectors, importCustomSelectorsFromCSSAST(from));
}
if (type === 'css') {
return Object.assign(customSelectors, await importCustomSelectorsFromCSSFile(from));
}
if (type === 'js') {
return Object.assign(customSelectors, await importCustomSelectorsFromJSFile(from));
}
if (type === 'json') {
return Object.assign(customSelectors, await importCustomSelectorsFromJSONFile(from));
}
return Object.assign(customSelectors, importCustomSelectorsFromObject(await source));
}, Promise.resolve({}));
}
/* Helper utilities
/* ========================================================================== */
const readFile = from => new Promise((resolve, reject) => {
fs.readFile(from, 'utf8', (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
const readJSON = async from => JSON.parse(await readFile(from));
/* Import Custom Selectors from CSS File
/* ========================================================================== */
async function exportCustomSelectorsToCssFile(to, customSelectors) {
const cssContent = Object.keys(customSelectors).reduce((cssLines, name) => {
cssLines.push(`@custom-selector ${name} ${customSelectors[name]};`);
return cssLines;
}, []).join('\n');
const css = `${cssContent}\n`;
await writeFile(to, css);
}
/* Import Custom Selectors from JSON file
/* ========================================================================== */
async function exportCustomSelectorsToJsonFile(to, customSelectors) {
const jsonContent = JSON.stringify({
'custom-selectors': customSelectors
}, null, ' ');
const json = `${jsonContent}\n`;
await writeFile(to, json);
}
/* Import Custom Selectors from Common JS file
/* ========================================================================== */
async function exportCustomSelectorsToCjsFile(to, customSelectors) {
const jsContents = Object.keys(customSelectors).reduce((jsLines, name) => {
jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(customSelectors[name])}'`);
return jsLines;
}, []).join(',\n');
const js = `module.exports = {\n\tcustomSelectors: {\n${jsContents}\n\t}\n};\n`;
await writeFile(to, js);
}
/* Import Custom Selectors from Module JS file
/* ========================================================================== */
async function exportCustomSelectorsToMjsFile(to, customSelectors) {
const mjsContents = Object.keys(customSelectors).reduce((mjsLines, name) => {
mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(customSelectors[name])}'`);
return mjsLines;
}, []).join(',\n');
const mjs = `export const customSelectors = {\n${mjsContents}\n};\n`;
await writeFile(to, mjs);
}
/* Export Custom Selectors to Destinations
/* ========================================================================== */
function exportCustomSelectorsToDestinations(customSelectors, destinations) {
return Promise.all(destinations.map(async destination => {
if (destination instanceof Function) {
await destination(defaultCustomSelectorsToJSON(customSelectors));
} else {
// read the destination as an object
const opts = destination === Object(destination) ? destination : {
to: String(destination)
}; // transformer for custom selectors into a JSON-compatible object
const toJSON = opts.toJSON || defaultCustomSelectorsToJSON;
if ('customSelectors' in opts) {
// write directly to an object as customSelectors
opts.customSelectors = toJSON(customSelectors);
} else if ('custom-selectors' in opts) {
// write directly to an object as custom-selectors
opts['custom-selectors'] = toJSON(customSelectors);
} else {
// destination pathname
const to = String(opts.to || ''); // type of file being written to
const type = (opts.type || path.extname(opts.to).slice(1)).toLowerCase(); // transformed custom selectors
const customSelectorsJSON = toJSON(customSelectors);
if (type === 'css') {
await exportCustomSelectorsToCssFile(to, customSelectorsJSON);
}
if (type === 'js') {
await exportCustomSelectorsToCjsFile(to, customSelectorsJSON);
}
if (type === 'json') {
await exportCustomSelectorsToJsonFile(to, customSelectorsJSON);
}
if (type === 'mjs') {
await exportCustomSelectorsToMjsFile(to, customSelectorsJSON);
}
}
}
}));
}
/* Helper utilities
/* ========================================================================== */
const defaultCustomSelectorsToJSON = customSelectors => {
return Object.keys(customSelectors).reduce((customSelectorsJSON, key) => {
customSelectorsJSON[key] = String(customSelectors[key]);
return customSelectorsJSON;
}, {});
};
const writeFile = (to, text) => new Promise((resolve, reject) => {
fs.writeFile(to, text, error => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
const escapeForJS = string => string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
const postcssCustomSelectors = opts => {
// whether to preserve custom selectors and rules using them
const preserve = Boolean(Object(opts).preserve); // sources to import custom selectors from
const importFrom = [].concat(Object(opts).importFrom || []); // destinations to export custom selectors to
const exportTo = [].concat(Object(opts).exportTo || []); // promise any custom selectors are imported
const customSelectorsPromise = importCustomSelectorsFromSources(importFrom);
return {
postcssPlugin: 'postcss-custom-selectors',
async Once(root) {
const customProperties = Object.assign({}, await customSelectorsPromise, getCustomSelectors(root, {
preserve
}));
await exportCustomSelectorsToDestinations(customProperties, exportTo);
transformRules(root, customProperties, {
preserve
});
}
};
};
postcssCustomSelectors.postcss = true;
export default postcssCustomSelectors;
//# sourceMappingURL=index.es.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
{
"name": "postcss-custom-selectors",
"version": "6.0.0",
"description": "Use Custom Selectors in CSS",
"author": "Jonathan Neal <jonathantneal@hotmail.com>",
"contributors": [
"yisi",
"Maxime Thirouin"
],
"license": "MIT",
"repository": "postcss/postcss-custom-selectors",
"homepage": "https://github.com/postcss/postcss-custom-selectors#readme",
"bugs": "https://github.com/postcss/postcss-custom-selectors/issues",
"main": "index.cjs.js",
"module": "index.es.mjs",
"files": [
"index.cjs.js",
"index.cjs.js.map",
"index.es.mjs",
"index.es.mjs.map"
],
"scripts": {
"prepublishOnly": "npm test",
"pretest": "rollup -c .rollup.js --silent",
"test": "echo 'Running tests...'; npm run test:js && npm run test:tape",
"test:js": "eslint *.js lib/*.js --cache --ignore-path .gitignore --quiet",
"test:tape": "postcss-tape"
},
"engines": {
"node": ">=10.0.0"
},
"dependencies": {
"postcss-selector-parser": "^6.0.4"
},
"devDependencies": {
"@babel/core": "^7.1.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/preset-env": "^7.1.0",
"@rollup/plugin-babel": "^5.2.1",
"babel-eslint": "^10.0.0",
"eslint": "^7.0.0",
"eslint-config-dev": "^2.0.0",
"postcss": "^8.1.2",
"postcss-tape": "^6.0.0",
"pre-commit": "^1.2.2",
"rollup": "^2.0.0"
},
"peerDependencies": {
"postcss": "^8.1.2"
},
"eslintConfig": {
"extends": "dev",
"parser": "babel-eslint"
},
"keywords": [
"postcss",
"css",
"postcss-plugin",
"custom",
"selectors",
"w3c",
"csswg",
"extensions",
"declarative",
"rule",
"atrule",
"at-rule",
"specification"
]
}