diff --git a/.github/ISSUE_TEMPLATE/css-issue.yml b/.github/ISSUE_TEMPLATE/css-issue.yml index 96dcbbab85..b6f4ba6c96 100644 --- a/.github/ISSUE_TEMPLATE/css-issue.yml +++ b/.github/ISSUE_TEMPLATE/css-issue.yml @@ -110,8 +110,8 @@ body: - PostCSS Logical Overscroll Behavior - PostCSS Logical Resize - PostCSS Logical Viewport Units - - PostCSS Media Queries Aspect-Ratio Number Values - PostCSS Media MinMax + - PostCSS Media Queries Aspect-Ratio Number Values - PostCSS Minify - PostCSS Nested Calc - PostCSS Nesting @@ -121,18 +121,20 @@ body: - PostCSS Place - PostCSS Position Area Property - PostCSS Progressive Custom Properties + - PostCSS Property Rule Prelude List - PostCSS Pseudo Class Any Link - PostCSS Random Function - PostCSS Rebase URL - - PostCSS Rewrite URL - PostCSS Rebeccapurple - PostCSS Relative Color Syntax - PostCSS Replace Overflow Wrap + - PostCSS Rewrite URL - PostCSS Scope Pseudo Class - PostCSS Selector Not - PostCSS Sign Functions - PostCSS Slow Plugins - PostCSS Stepped Value Functions + - PostCSS Syntax Descriptor Syntax Production - PostCSS System Ui Font Family - PostCSS Text Decoration Shorthand - PostCSS Trigonometric Functions diff --git a/.github/ISSUE_TEMPLATE/plugin-issue.yml b/.github/ISSUE_TEMPLATE/plugin-issue.yml index fdb488208d..70d7be639d 100644 --- a/.github/ISSUE_TEMPLATE/plugin-issue.yml +++ b/.github/ISSUE_TEMPLATE/plugin-issue.yml @@ -107,8 +107,8 @@ body: - PostCSS Logical Overscroll Behavior - PostCSS Logical Resize - PostCSS Logical Viewport Units - - PostCSS Media Queries Aspect-Ratio Number Values - PostCSS Media MinMax + - PostCSS Media Queries Aspect-Ratio Number Values - PostCSS Minify - PostCSS Nested Calc - PostCSS Nesting @@ -118,18 +118,20 @@ body: - PostCSS Place - PostCSS Position Area Property - PostCSS Progressive Custom Properties + - PostCSS Property Rule Prelude List - PostCSS Pseudo Class Any Link - PostCSS Random Function - PostCSS Rebase URL - - PostCSS Rewrite URL - PostCSS Rebeccapurple - PostCSS Relative Color Syntax - PostCSS Replace Overflow Wrap + - PostCSS Rewrite URL - PostCSS Scope Pseudo Class - PostCSS Selector Not - PostCSS Sign Functions - PostCSS Slow Plugins - PostCSS Stepped Value Functions + - PostCSS Syntax Descriptor Syntax Production - PostCSS System Ui Font Family - PostCSS Text Decoration Shorthand - PostCSS Trigonometric Functions diff --git a/.github/labeler.yml b/.github/labeler.yml index 98cb932c46..7219806efb 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -407,6 +407,12 @@ - plugins/postcss-progressive-custom-properties/** - experimental/postcss-progressive-custom-properties/** +"plugins/postcss-property-rule-prelude-list": + - changed-files: + - any-glob-to-any-file: + - plugins/postcss-property-rule-prelude-list/** + - experimental/postcss-property-rule-prelude-list/** + "plugins/postcss-pseudo-class-any-link": - changed-files: - any-glob-to-any-file: @@ -479,6 +485,12 @@ - plugins/postcss-stepped-value-functions/** - experimental/postcss-stepped-value-functions/** +"plugins/syntax-descriptor-syntax-production": + - changed-files: + - any-glob-to-any-file: + - plugins/postcss-syntax-descriptor-syntax-production/** + - experimental/postcss-syntax-descriptor-syntax-production/** + "plugins/postcss-text-decoration-shorthand": - changed-files: - any-glob-to-any-file: diff --git a/package-lock.json b/package-lock.json index 21f11804ef..abcc53f4df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2096,6 +2096,10 @@ "resolved": "plugins/postcss-progressive-custom-properties", "link": true }, + "node_modules/@csstools/postcss-property-rule-prelude-list": { + "resolved": "plugins/postcss-property-rule-prelude-list", + "link": true + }, "node_modules/@csstools/postcss-random-function": { "resolved": "plugins/postcss-random-function", "link": true @@ -2128,6 +2132,10 @@ "resolved": "plugins/postcss-stepped-value-functions", "link": true }, + "node_modules/@csstools/postcss-syntax-descriptor-syntax-production": { + "resolved": "plugins/postcss-syntax-descriptor-syntax-production", + "link": true + }, "node_modules/@csstools/postcss-system-ui-font-family": { "resolved": "plugins/postcss-system-ui-font-family", "link": true @@ -11769,6 +11777,34 @@ "postcss": "^8.4" } }, + "plugins/postcss-property-rule-prelude-list": { + "name": "@csstools/postcss-property-rule-prelude-list", + "version": "0.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "devDependencies": { + "@csstools/postcss-tape": "*" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "plugins/postcss-pseudo-class-any-link": { "version": "10.0.1", "funding": [ @@ -12047,6 +12083,33 @@ "postcss": "^8.4" } }, + "plugins/postcss-syntax-descriptor-syntax-production": { + "name": "@csstools/postcss-syntax-descriptor-syntax-production", + "version": "0.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-tokenizer": "^3.0.4" + }, + "devDependencies": { + "@csstools/postcss-tape": "*" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "plugins/postcss-system-ui-font-family": { "name": "@csstools/postcss-system-ui-font-family", "version": "1.0.0", diff --git a/plugins/postcss-property-rule-prelude-list/.gitignore b/plugins/postcss-property-rule-prelude-list/.gitignore new file mode 100644 index 0000000000..e5b28db4ad --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/.gitignore @@ -0,0 +1,6 @@ +node_modules +package-lock.json +yarn.lock +*.result.css +*.result.css.map +*.result.html diff --git a/plugins/postcss-property-rule-prelude-list/.nvmrc b/plugins/postcss-property-rule-prelude-list/.nvmrc new file mode 100644 index 0000000000..28d6ff1c82 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/.nvmrc @@ -0,0 +1 @@ +v25.1.0 diff --git a/plugins/postcss-property-rule-prelude-list/CHANGELOG.md b/plugins/postcss-property-rule-prelude-list/CHANGELOG.md new file mode 100644 index 0000000000..ff38c3b481 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changes to PostCSS Property Rule Prelude List + +### Unreleased (major) + +- Initial version diff --git a/plugins/postcss-property-rule-prelude-list/INSTALL.md b/plugins/postcss-property-rule-prelude-list/INSTALL.md new file mode 100644 index 0000000000..2bd343587a --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/INSTALL.md @@ -0,0 +1,235 @@ +# Installing PostCSS Property Rule Prelude List + +[PostCSS Property Rule Prelude List] runs in all Node environments, with special instructions for: + +- [Node](#node) +- [PostCSS CLI](#postcss-cli) +- [PostCSS Load Config](#postcss-load-config) +- [Webpack](#webpack) +- [Next.js](#nextjs) +- [Gulp](#gulp) +- [Grunt](#grunt) + + + +## Node + +Add [PostCSS Property Rule Prelude List] to your project: + +```bash +npm install postcss @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +// commonjs +const postcss = require('postcss'); +const postcssPropertyRulePreludeList = require('@csstools/postcss-property-rule-prelude-list'); + +postcss([ + postcssPropertyRulePreludeList(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +```js +// esm +import postcss from 'postcss'; +import postcssPropertyRulePreludeList from '@csstools/postcss-property-rule-prelude-list'; + +postcss([ + postcssPropertyRulePreludeList(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +## PostCSS CLI + +Add [PostCSS CLI] to your project: + +```bash +npm install postcss-cli @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use [PostCSS Property Rule Prelude List] in your `postcss.config.js` configuration file: + +```js +const postcssPropertyRulePreludeList = require('@csstools/postcss-property-rule-prelude-list'); + +module.exports = { + plugins: [ + postcssPropertyRulePreludeList(/* pluginOptions */) + ] +} +``` + +## PostCSS Load Config + +If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). + +```bash +npm install @csstools/postcss-property-rule-prelude-list --save-dev +``` + +`package.json`: + +```json +{ + "postcss": { + "plugins": { + "@csstools/postcss-property-rule-prelude-list": {} + } + } +} +``` + +`.postcssrc.json`: + +```json +{ + "plugins": { + "@csstools/postcss-property-rule-prelude-list": {} + } +} +``` + +_See the [README of `postcss-load-config`](https://github.com/postcss/postcss-load-config#usage) for more usage options._ + +## Webpack + +_Webpack version 5_ + +Add [PostCSS Loader] to your project: + +```bash +npm install postcss-loader @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use [PostCSS Property Rule Prelude List] in your Webpack configuration: + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + use: [ + "style-loader", + { + loader: "css-loader", + options: { importLoaders: 1 }, + }, + { + loader: "postcss-loader", + options: { + postcssOptions: { + plugins: [ + // Other plugins, + [ + "@csstools/postcss-property-rule-prelude-list", + { + // Options + }, + ], + ], + }, + }, + }, + ], + }, + ], + }, +}; +``` + +## Next.js + +Read the instructions on how to [customize the PostCSS configuration in Next.js](https://nextjs.org/docs/advanced-features/customizing-postcss-config) + +```bash +npm install @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use [PostCSS Property Rule Prelude List] in your `postcss.config.json` file: + +```json +{ + "plugins": [ + "@csstools/postcss-property-rule-prelude-list" + ] +} +``` + +```json5 +{ + "plugins": [ + [ + "@csstools/postcss-property-rule-prelude-list", + { + // Optionally add plugin options + } + ] + ] +} +``` + +## Gulp + +Add [Gulp PostCSS] to your project: + +```bash +npm install gulp-postcss @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use [PostCSS Property Rule Prelude List] in your Gulpfile: + +```js +const postcss = require('gulp-postcss'); +const postcssPropertyRulePreludeList = require('@csstools/postcss-property-rule-prelude-list'); + +gulp.task('css', function () { + var plugins = [ + postcssPropertyRulePreludeList(/* pluginOptions */) + ]; + + return gulp.src('./src/*.css') + .pipe(postcss(plugins)) + .pipe(gulp.dest('.')); +}); +``` + +## Grunt + +Add [Grunt PostCSS] to your project: + +```bash +npm install grunt-postcss @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use [PostCSS Property Rule Prelude List] in your Gruntfile: + +```js +const postcssPropertyRulePreludeList = require('@csstools/postcss-property-rule-prelude-list'); + +grunt.loadNpmTasks('grunt-postcss'); + +grunt.initConfig({ + postcss: { + options: { + processors: [ + postcssPropertyRulePreludeList(/* pluginOptions */) + ] + }, + dist: { + src: '*.css' + } + } +}); +``` + +[Gulp PostCSS]: https://github.com/postcss/gulp-postcss +[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss +[PostCSS]: https://github.com/postcss/postcss +[PostCSS CLI]: https://github.com/postcss/postcss-cli +[PostCSS Loader]: https://github.com/postcss/postcss-loader +[PostCSS Property Rule Prelude List]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-property-rule-prelude-list +[Next.js]: https://nextjs.org diff --git a/plugins/postcss-property-rule-prelude-list/LICENSE.md b/plugins/postcss-property-rule-prelude-list/LICENSE.md new file mode 100644 index 0000000000..e8ae93b9f9 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/LICENSE.md @@ -0,0 +1,18 @@ +MIT No Attribution (MIT-0) + +Copyright © CSSTools Contributors + +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. + +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. diff --git a/plugins/postcss-property-rule-prelude-list/README.md b/plugins/postcss-property-rule-prelude-list/README.md new file mode 100644 index 0000000000..36614d645c --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/README.md @@ -0,0 +1,69 @@ +# PostCSS Property Rule Prelude List [PostCSS Logo][PostCSS] + +[npm version][npm-url] [Build Status][cli-url] [Discord][discord]

[Baseline Status][css-url] [CSS Standard Status][css-url] + +```bash +npm install @csstools/postcss-property-rule-prelude-list --save-dev +``` + +[PostCSS Property Rule Prelude List] lets you declare a list of custom properties in a single `@property` rule following the [CSS Specification]. + +```css +@property --color-a, --color-b { + inherits: true; + initial-value: black; + syntax: ""; +} + +/* becomes */ + +@property --color-a { + inherits: true; + initial-value: black; + syntax: ""; +} +@property --color-b { + inherits: true; + initial-value: black; + syntax: ""; +} +``` + +## Usage + +Add [PostCSS Property Rule Prelude List] to your project: + +```bash +npm install postcss @csstools/postcss-property-rule-prelude-list --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssPropertyRulePreludeList = require('@csstools/postcss-property-rule-prelude-list'); + +postcss([ + postcssPropertyRulePreludeList(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +[PostCSS Property Rule Prelude List] runs in all Node environments, with special +instructions for: + +- [Node](INSTALL.md#node) +- [PostCSS CLI](INSTALL.md#postcss-cli) +- [PostCSS Load Config](INSTALL.md#postcss-load-config) +- [Webpack](INSTALL.md#webpack) +- [Next.js](INSTALL.md#nextjs) +- [Gulp](INSTALL.md#gulp) +- [Grunt](INSTALL.md#grunt) + +[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test +[css-url]: https://cssdb.org/#property-rule-prelude-list +[discord]: https://discord.gg/bUadyRwkJS +[npm-url]: https://www.npmjs.com/package/@csstools/postcss-property-rule-prelude-list + +[PostCSS]: https://github.com/postcss/postcss +[PostCSS Property Rule Prelude List]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-property-rule-prelude-list +[CSS Specification]: https://github.com/w3c/csswg-drafts/issues/7523#issuecomment-3683970305 diff --git a/plugins/postcss-property-rule-prelude-list/api-extractor.json b/plugins/postcss-property-rule-prelude-list/api-extractor.json new file mode 100644 index 0000000000..42058be517 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../api-extractor.json" +} diff --git a/plugins/postcss-property-rule-prelude-list/dist/index.cjs b/plugins/postcss-property-rule-prelude-list/dist/index.cjs new file mode 100644 index 0000000000..64a2db2d78 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/dist/index.cjs @@ -0,0 +1 @@ +"use strict";var s=require("@csstools/css-parser-algorithms"),e=require("@csstools/css-tokenizer");const r=/^property$/i,creator=()=>({postcssPlugin:"postcss-property-rule-prelude-list",AtRule(t){if(!r.test(t.name))return;if(!t.params.includes(","))return;const o=s.parseCommaSeparatedListOfComponentValues(e.tokenize({css:t.params}));o.length<2||(o.forEach(e=>{t.cloneBefore({params:s.stringify([e]).trim()})}),t.remove())}});creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-property-rule-prelude-list/dist/index.d.ts b/plugins/postcss-property-rule-prelude-list/dist/index.d.ts new file mode 100644 index 0000000000..27d0f244b6 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/dist/index.d.ts @@ -0,0 +1,9 @@ +import type { PluginCreator } from 'postcss'; + +declare const creator: PluginCreator; +export default creator; + +/** postcss-property-rule-prelude-list plugin options */ +export declare type pluginOptions = never; + +export { } diff --git a/plugins/postcss-property-rule-prelude-list/dist/index.mjs b/plugins/postcss-property-rule-prelude-list/dist/index.mjs new file mode 100644 index 0000000000..26a8de6c15 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/dist/index.mjs @@ -0,0 +1 @@ +import{parseCommaSeparatedListOfComponentValues as s,stringify as r}from"@csstools/css-parser-algorithms";import{tokenize as t}from"@csstools/css-tokenizer";const e=/^property$/i,creator=()=>({postcssPlugin:"postcss-property-rule-prelude-list",AtRule(o){if(!e.test(o.name))return;if(!o.params.includes(","))return;const p=s(t({css:o.params}));p.length<2||(p.forEach(s=>{o.cloneBefore({params:r([s]).trim()})}),o.remove())}});creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-property-rule-prelude-list/docs/README.md b/plugins/postcss-property-rule-prelude-list/docs/README.md new file mode 100644 index 0000000000..eb262e2c2b --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/docs/README.md @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + +
+ +[] lets you declare a list of custom properties in a single `@property` rule following the [CSS Specification]. + +```css + + +/* becomes */ + + +``` + + + + + + +[CSS Specification]: diff --git a/plugins/postcss-property-rule-prelude-list/package.json b/plugins/postcss-property-rule-prelude-list/package.json new file mode 100644 index 0000000000..6aed94a503 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/package.json @@ -0,0 +1,87 @@ +{ + "name": "@csstools/postcss-property-rule-prelude-list", + "description": "Declare a list of custom properties in a single at-property rule", + "version": "0.0.0", + "contributors": [ + { + "name": "Antonio Laguna", + "email": "antonio@laguna.es", + "url": "https://antonio.laguna.es" + }, + { + "name": "Romain Menke", + "email": "romainmenke@gmail.com" + } + ], + "license": "MIT-0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "require": { + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "CHANGELOG.md", + "LICENSE.md", + "README.md", + "dist" + ], + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "peerDependencies": { + "postcss": "^8.4" + }, + "devDependencies": { + "@csstools/postcss-tape": "*" + }, + "scripts": { + "build": "rollup -c ../../rollup/default.mjs", + "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", + "lint": "node ../../.github/bin/format-package-json.mjs", + "prepublishOnly": "npm run build && npm run test", + "test": "node --test", + "test:rewrite-expects": "REWRITE_EXPECTS=true node --test" + }, + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-property-rule-prelude-list#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/csstools/postcss-plugins.git", + "directory": "plugins/postcss-property-rule-prelude-list" + }, + "bugs": "https://github.com/csstools/postcss-plugins/issues", + "keywords": [ + "postcss-plugin" + ], + "csstools": { + "cssdbId": "property-rule-prelude-list", + "exportName": "postcssPropertyRulePreludeList", + "humanReadableName": "PostCSS Property Rule Prelude List", + "specUrl": "https://github.com/w3c/csswg-drafts/issues/7523#issuecomment-3683970305" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/plugins/postcss-property-rule-prelude-list/src/index.ts b/plugins/postcss-property-rule-prelude-list/src/index.ts new file mode 100644 index 0000000000..4ce1a3b87f --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/src/index.ts @@ -0,0 +1,41 @@ +import { parseCommaSeparatedListOfComponentValues, stringify } from '@csstools/css-parser-algorithms'; +import { tokenize } from '@csstools/css-tokenizer'; +import type { PluginCreator } from 'postcss'; + +/** postcss-property-rule-prelude-list plugin options */ +export type pluginOptions = never; + +const IS_AT_PROPERTY_REGEX = /^property$/i; + +const creator: PluginCreator = () => { + + return { + postcssPlugin: 'postcss-property-rule-prelude-list', + AtRule(atRule): void { + if (!IS_AT_PROPERTY_REGEX.test(atRule.name)) { + return; + } + + if (!atRule.params.includes(',')) { + return; + } + + const list = parseCommaSeparatedListOfComponentValues(tokenize({ css: atRule.params })); + if (list.length < 2) { + return; + } + + list.forEach((params) => { + atRule.cloneBefore({ + params: stringify([params]).trim(), + }); + }) + + atRule.remove(); + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-property-rule-prelude-list/test/_import.mjs b/plugins/postcss-property-rule-prelude-list/test/_import.mjs new file mode 100644 index 0000000000..454b80a4d4 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/_import.mjs @@ -0,0 +1,10 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import plugin from '@csstools/postcss-property-rule-prelude-list'; + +test('import', () => { + plugin(); + assert.ok(plugin.postcss, 'should have "postcss flag"'); + assert.equal(typeof plugin, 'function', 'should return a function'); +}); + diff --git a/plugins/postcss-property-rule-prelude-list/test/_require.cjs b/plugins/postcss-property-rule-prelude-list/test/_require.cjs new file mode 100644 index 0000000000..2af37a9006 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/_require.cjs @@ -0,0 +1,9 @@ +const assert = require('node:assert/strict'); +const test = require('node:test'); +const plugin = require('@csstools/postcss-property-rule-prelude-list'); + +test('require', () => { + plugin(); + assert.ok(plugin.postcss, 'should have "postcss flag"'); + assert.equal(typeof plugin, 'function', 'should return a function'); +}); diff --git a/plugins/postcss-property-rule-prelude-list/test/_tape.mjs b/plugins/postcss-property-rule-prelude-list/test/_tape.mjs new file mode 100644 index 0000000000..60eb29bfee --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/_tape.mjs @@ -0,0 +1,11 @@ +import { postcssTape } from '@csstools/postcss-tape'; +import plugin from '@csstools/postcss-property-rule-prelude-list'; + +postcssTape(plugin)({ + basic: { + message: 'supports basic usage', + }, + 'examples/example': { + message: 'minimal example', + }, +}); diff --git a/plugins/postcss-property-rule-prelude-list/test/basic.css b/plugins/postcss-property-rule-prelude-list/test/basic.css new file mode 100644 index 0000000000..37174ad9e9 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/basic.css @@ -0,0 +1,23 @@ +@property --color-a, --color-b { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-c something, --color-d { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-e, --color-f, --color-g, --color-h { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-i something(a, b) { + inherits: true; + initial-value: black; + syntax: ""; +} diff --git a/plugins/postcss-property-rule-prelude-list/test/basic.expect.css b/plugins/postcss-property-rule-prelude-list/test/basic.expect.css new file mode 100644 index 0000000000..64f03c799d --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/basic.expect.css @@ -0,0 +1,53 @@ +@property --color-a { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-b { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-c something { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-d { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-e { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-f { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-g { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-h { + inherits: true; + initial-value: black; + syntax: ""; +} + +@property --color-i something(a, b) { + inherits: true; + initial-value: black; + syntax: ""; +} diff --git a/plugins/postcss-property-rule-prelude-list/test/examples/example.css b/plugins/postcss-property-rule-prelude-list/test/examples/example.css new file mode 100644 index 0000000000..92a6bb4491 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/examples/example.css @@ -0,0 +1,5 @@ +@property --color-a, --color-b { + inherits: true; + initial-value: black; + syntax: ""; +} diff --git a/plugins/postcss-property-rule-prelude-list/test/examples/example.expect.css b/plugins/postcss-property-rule-prelude-list/test/examples/example.expect.css new file mode 100644 index 0000000000..492cd8616d --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/test/examples/example.expect.css @@ -0,0 +1,10 @@ +@property --color-a { + inherits: true; + initial-value: black; + syntax: ""; +} +@property --color-b { + inherits: true; + initial-value: black; + syntax: ""; +} diff --git a/plugins/postcss-property-rule-prelude-list/tsconfig.json b/plugins/postcss-property-rule-prelude-list/tsconfig.json new file mode 100644 index 0000000000..500af6d266 --- /dev/null +++ b/plugins/postcss-property-rule-prelude-list/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": ".", + "strict": true + }, + "include": ["./src/**/*"], + "exclude": ["dist"] +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/.gitignore b/plugins/postcss-syntax-descriptor-syntax-production/.gitignore new file mode 100644 index 0000000000..e5b28db4ad --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/.gitignore @@ -0,0 +1,6 @@ +node_modules +package-lock.json +yarn.lock +*.result.css +*.result.css.map +*.result.html diff --git a/plugins/postcss-syntax-descriptor-syntax-production/.nvmrc b/plugins/postcss-syntax-descriptor-syntax-production/.nvmrc new file mode 100644 index 0000000000..28d6ff1c82 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/.nvmrc @@ -0,0 +1 @@ +v25.1.0 diff --git a/plugins/postcss-syntax-descriptor-syntax-production/CHANGELOG.md b/plugins/postcss-syntax-descriptor-syntax-production/CHANGELOG.md new file mode 100644 index 0000000000..6e8575a65f --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changes to PostCSS Syntax Descriptor Syntax Production + +### Unreleased (major) + +- Initial version diff --git a/plugins/postcss-syntax-descriptor-syntax-production/INSTALL.md b/plugins/postcss-syntax-descriptor-syntax-production/INSTALL.md new file mode 100644 index 0000000000..c490220326 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/INSTALL.md @@ -0,0 +1,235 @@ +# Installing PostCSS Syntax Descriptor Syntax Production + +[PostCSS Syntax Descriptor Syntax Production] runs in all Node environments, with special instructions for: + +- [Node](#node) +- [PostCSS CLI](#postcss-cli) +- [PostCSS Load Config](#postcss-load-config) +- [Webpack](#webpack) +- [Next.js](#nextjs) +- [Gulp](#gulp) +- [Grunt](#grunt) + + + +## Node + +Add [PostCSS Syntax Descriptor Syntax Production] to your project: + +```bash +npm install postcss @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +// commonjs +const postcss = require('postcss'); +const postcssSyntaxDescriptorSyntaxProduction = require('@csstools/postcss-syntax-descriptor-syntax-production'); + +postcss([ + postcssSyntaxDescriptorSyntaxProduction(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +```js +// esm +import postcss from 'postcss'; +import postcssSyntaxDescriptorSyntaxProduction from '@csstools/postcss-syntax-descriptor-syntax-production'; + +postcss([ + postcssSyntaxDescriptorSyntaxProduction(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +## PostCSS CLI + +Add [PostCSS CLI] to your project: + +```bash +npm install postcss-cli @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use [PostCSS Syntax Descriptor Syntax Production] in your `postcss.config.js` configuration file: + +```js +const postcssSyntaxDescriptorSyntaxProduction = require('@csstools/postcss-syntax-descriptor-syntax-production'); + +module.exports = { + plugins: [ + postcssSyntaxDescriptorSyntaxProduction(/* pluginOptions */) + ] +} +``` + +## PostCSS Load Config + +If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). + +```bash +npm install @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +`package.json`: + +```json +{ + "postcss": { + "plugins": { + "@csstools/postcss-syntax-descriptor-syntax-production": {} + } + } +} +``` + +`.postcssrc.json`: + +```json +{ + "plugins": { + "@csstools/postcss-syntax-descriptor-syntax-production": {} + } +} +``` + +_See the [README of `postcss-load-config`](https://github.com/postcss/postcss-load-config#usage) for more usage options._ + +## Webpack + +_Webpack version 5_ + +Add [PostCSS Loader] to your project: + +```bash +npm install postcss-loader @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use [PostCSS Syntax Descriptor Syntax Production] in your Webpack configuration: + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + use: [ + "style-loader", + { + loader: "css-loader", + options: { importLoaders: 1 }, + }, + { + loader: "postcss-loader", + options: { + postcssOptions: { + plugins: [ + // Other plugins, + [ + "@csstools/postcss-syntax-descriptor-syntax-production", + { + // Options + }, + ], + ], + }, + }, + }, + ], + }, + ], + }, +}; +``` + +## Next.js + +Read the instructions on how to [customize the PostCSS configuration in Next.js](https://nextjs.org/docs/advanced-features/customizing-postcss-config) + +```bash +npm install @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use [PostCSS Syntax Descriptor Syntax Production] in your `postcss.config.json` file: + +```json +{ + "plugins": [ + "@csstools/postcss-syntax-descriptor-syntax-production" + ] +} +``` + +```json5 +{ + "plugins": [ + [ + "@csstools/postcss-syntax-descriptor-syntax-production", + { + // Optionally add plugin options + } + ] + ] +} +``` + +## Gulp + +Add [Gulp PostCSS] to your project: + +```bash +npm install gulp-postcss @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use [PostCSS Syntax Descriptor Syntax Production] in your Gulpfile: + +```js +const postcss = require('gulp-postcss'); +const postcssSyntaxDescriptorSyntaxProduction = require('@csstools/postcss-syntax-descriptor-syntax-production'); + +gulp.task('css', function () { + var plugins = [ + postcssSyntaxDescriptorSyntaxProduction(/* pluginOptions */) + ]; + + return gulp.src('./src/*.css') + .pipe(postcss(plugins)) + .pipe(gulp.dest('.')); +}); +``` + +## Grunt + +Add [Grunt PostCSS] to your project: + +```bash +npm install grunt-postcss @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use [PostCSS Syntax Descriptor Syntax Production] in your Gruntfile: + +```js +const postcssSyntaxDescriptorSyntaxProduction = require('@csstools/postcss-syntax-descriptor-syntax-production'); + +grunt.loadNpmTasks('grunt-postcss'); + +grunt.initConfig({ + postcss: { + options: { + processors: [ + postcssSyntaxDescriptorSyntaxProduction(/* pluginOptions */) + ] + }, + dist: { + src: '*.css' + } + } +}); +``` + +[Gulp PostCSS]: https://github.com/postcss/gulp-postcss +[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss +[PostCSS]: https://github.com/postcss/postcss +[PostCSS CLI]: https://github.com/postcss/postcss-cli +[PostCSS Loader]: https://github.com/postcss/postcss-loader +[PostCSS Syntax Descriptor Syntax Production]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-syntax-descriptor-syntax-production +[Next.js]: https://nextjs.org diff --git a/plugins/postcss-syntax-descriptor-syntax-production/LICENSE.md b/plugins/postcss-syntax-descriptor-syntax-production/LICENSE.md new file mode 100644 index 0000000000..e8ae93b9f9 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/LICENSE.md @@ -0,0 +1,18 @@ +MIT No Attribution (MIT-0) + +Copyright © CSSTools Contributors + +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. + +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. diff --git a/plugins/postcss-syntax-descriptor-syntax-production/README.md b/plugins/postcss-syntax-descriptor-syntax-production/README.md new file mode 100644 index 0000000000..587018faee --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/README.md @@ -0,0 +1,92 @@ +# PostCSS Syntax Descriptor Syntax Production [PostCSS Logo][PostCSS] + +[npm version][npm-url] [Build Status][cli-url] [Discord][discord]

[Baseline Status][css-url] [CSS Standard Status][css-url] + +```bash +npm install @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +[PostCSS Syntax Descriptor Syntax Production] lets you use the `` production in `syntax` descriptors following the [CSS Specification]. + +```css +@property --color { + inherits: true; + initial-value: black; + syntax: ; +} + +/* becomes */ + +@property --color { + inherits: true; + initial-value: black; + color: ""; +} +``` + +## Usage + +Add [PostCSS Syntax Descriptor Syntax Production] to your project: + +```bash +npm install postcss @csstools/postcss-syntax-descriptor-syntax-production --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssSyntaxDescriptorSyntaxProduction = require('@csstools/postcss-syntax-descriptor-syntax-production'); + +postcss([ + postcssSyntaxDescriptorSyntaxProduction(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +[PostCSS Syntax Descriptor Syntax Production] runs in all Node environments, with special +instructions for: + +- [Node](INSTALL.md#node) +- [PostCSS CLI](INSTALL.md#postcss-cli) +- [PostCSS Load Config](INSTALL.md#postcss-load-config) +- [Webpack](INSTALL.md#webpack) +- [Next.js](INSTALL.md#nextjs) +- [Gulp](INSTALL.md#gulp) +- [Grunt](INSTALL.md#grunt) + +## Options + +### preserve + +The `preserve` option determines whether the original notation +is preserved. By default, it is not preserved. + +```js +postcssSyntaxDescriptorSyntaxProduction({ preserve: true }) +``` + +```css +@property --color { + inherits: true; + initial-value: black; + syntax: ; +} + +/* becomes */ + +@property --color { + inherits: true; + initial-value: black; + color: ""; + syntax: ; +} +``` + +[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test +[css-url]: https://cssdb.org/#syntax-descriptor-syntax-production +[discord]: https://discord.gg/bUadyRwkJS +[npm-url]: https://www.npmjs.com/package/@csstools/postcss-syntax-descriptor-syntax-production + +[PostCSS]: https://github.com/postcss/postcss +[PostCSS Syntax Descriptor Syntax Production]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-syntax-descriptor-syntax-production +[CSS Specification]: https://github.com/w3c/csswg-drafts/issues/11426#issuecomment-3657538113 diff --git a/plugins/postcss-syntax-descriptor-syntax-production/api-extractor.json b/plugins/postcss-syntax-descriptor-syntax-production/api-extractor.json new file mode 100644 index 0000000000..42058be517 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../api-extractor.json" +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/dist/index.cjs b/plugins/postcss-syntax-descriptor-syntax-production/dist/index.cjs new file mode 100644 index 0000000000..b898a58372 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/dist/index.cjs @@ -0,0 +1 @@ +"use strict";var e=require("@csstools/css-tokenizer");const s=/^property$/i,c=/^syntax$/i,creator=a=>{const t=Object.assign({preserve:!1},a);return{postcssPlugin:"postcss-syntax-descriptor-syntax-production",Declaration(a){if(!c.test(a.prop))return;if(!a.parent||"atrule"!==a.parent.type)return;if(!s.test(a.parent.name))return;const o=a.value,r=e.tokenize({css:a.value});let n="";r.forEach(s=>{e.isTokenComment(s)||(e.isTokenWhitespace(s)?n+=" ":n+=s[1])});let i='"';for(const e of n){const s=e.codePointAt(0);if(void 0!==s)switch(s){case 0:i+=String.fromCodePoint(65533);break;case 34:case 92:i+="\\"+String.fromCodePoint(s);break;case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 20:case 21:case 22:case 23:case 24:case 25:case 26:case 27:case 28:case 29:case 30:case 31:case 127:i+="\\"+s.toString(16)+" ";break;default:i+=String.fromCodePoint(s)}}i+='"',o!==i&&(a.cloneBefore({prop:"color",value:i}),t.preserve||a.remove())}}};creator.postcss=!0,module.exports=creator; diff --git a/plugins/postcss-syntax-descriptor-syntax-production/dist/index.d.ts b/plugins/postcss-syntax-descriptor-syntax-production/dist/index.d.ts new file mode 100644 index 0000000000..9e6a372973 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/dist/index.d.ts @@ -0,0 +1,12 @@ +import type { PluginCreator } from 'postcss'; + +declare const creator: PluginCreator; +export default creator; + +/** postcss-syntax-descriptor-syntax-production plugin options */ +export declare type pluginOptions = { + /** Preserve the original notation. default: false */ + preserve?: boolean; +}; + +export { } diff --git a/plugins/postcss-syntax-descriptor-syntax-production/dist/index.mjs b/plugins/postcss-syntax-descriptor-syntax-production/dist/index.mjs new file mode 100644 index 0000000000..a532e96084 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/dist/index.mjs @@ -0,0 +1 @@ +import{tokenize as e,isTokenComment as s,isTokenWhitespace as a}from"@csstools/css-tokenizer";const c=/^property$/i,t=/^syntax$/i,creator=r=>{const o=Object.assign({preserve:!1},r);return{postcssPlugin:"postcss-syntax-descriptor-syntax-production",Declaration(r){if(!t.test(r.prop))return;if(!r.parent||"atrule"!==r.parent.type)return;if(!c.test(r.parent.name))return;const n=r.value,i=e({css:r.value});let p="";i.forEach(e=>{s(e)||(a(e)?p+=" ":p+=e[1])});let f='"';for(const e of p){const s=e.codePointAt(0);if(void 0!==s)switch(s){case 0:f+=String.fromCodePoint(65533);break;case 34:case 92:f+="\\"+String.fromCodePoint(s);break;case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 20:case 21:case 22:case 23:case 24:case 25:case 26:case 27:case 28:case 29:case 30:case 31:case 127:f+="\\"+s.toString(16)+" ";break;default:f+=String.fromCodePoint(s)}}f+='"',n!==f&&(r.cloneBefore({prop:"color",value:f}),o.preserve||r.remove())}}};creator.postcss=!0;export{creator as default}; diff --git a/plugins/postcss-syntax-descriptor-syntax-production/docs/README.md b/plugins/postcss-syntax-descriptor-syntax-production/docs/README.md new file mode 100644 index 0000000000..bb1ef292d7 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/docs/README.md @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + +
+ +[] lets you use the `` production in `syntax` descriptors following the [CSS Specification]. + +```css + + +/* becomes */ + + +``` + + + + + +## Options + +### preserve + +The `preserve` option determines whether the original notation +is preserved. By default, it is not preserved. + +```js +({ preserve: true }) +``` + +```css + + +/* becomes */ + + +``` + + +[CSS Specification]: diff --git a/plugins/postcss-syntax-descriptor-syntax-production/package.json b/plugins/postcss-syntax-descriptor-syntax-production/package.json new file mode 100644 index 0000000000..903428fa1c --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/package.json @@ -0,0 +1,86 @@ +{ + "name": "@csstools/postcss-syntax-descriptor-syntax-production", + "description": "Use the syntax production in syntax descriptors", + "version": "0.0.0", + "contributors": [ + { + "name": "Antonio Laguna", + "email": "antonio@laguna.es", + "url": "https://antonio.laguna.es" + }, + { + "name": "Romain Menke", + "email": "romainmenke@gmail.com" + } + ], + "license": "MIT-0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "require": { + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "CHANGELOG.md", + "LICENSE.md", + "README.md", + "dist" + ], + "dependencies": { + "@csstools/css-tokenizer": "^3.0.4" + }, + "peerDependencies": { + "postcss": "^8.4" + }, + "devDependencies": { + "@csstools/postcss-tape": "*" + }, + "scripts": { + "build": "rollup -c ../../rollup/default.mjs", + "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", + "lint": "node ../../.github/bin/format-package-json.mjs", + "prepublishOnly": "npm run build && npm run test", + "test": "node --test", + "test:rewrite-expects": "REWRITE_EXPECTS=true node --test" + }, + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-syntax-descriptor-syntax-production#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/csstools/postcss-plugins.git", + "directory": "plugins/postcss-syntax-descriptor-syntax-production" + }, + "bugs": "https://github.com/csstools/postcss-plugins/issues", + "keywords": [ + "postcss-plugin" + ], + "csstools": { + "cssdbId": "syntax-descriptor-syntax-production", + "exportName": "postcssSyntaxDescriptorSyntaxProduction", + "humanReadableName": "PostCSS Syntax Descriptor Syntax Production", + "specUrl": "https://github.com/w3c/csswg-drafts/issues/11426#issuecomment-3657538113" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/src/index.ts b/plugins/postcss-syntax-descriptor-syntax-production/src/index.ts new file mode 100644 index 0000000000..3d78391434 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/src/index.ts @@ -0,0 +1,132 @@ +import { isTokenComment, isTokenWhitespace, tokenize } from '@csstools/css-tokenizer'; +import type { PluginCreator } from 'postcss'; + +/** postcss-syntax-descriptor-syntax-production plugin options */ +export type pluginOptions = { + /** Preserve the original notation. default: false */ + preserve?: boolean, +}; + +const IS_AT_PROPERTY_REGEX = /^property$/i; +const IS_SYNTAX_REGEX = /^syntax$/i; + +const creator: PluginCreator = (opts?: pluginOptions) => { + const options: pluginOptions = Object.assign( + // Default options + { + preserve: false, + }, + // Provided options + opts, + ); + + return { + postcssPlugin: 'postcss-syntax-descriptor-syntax-production', + Declaration(decl): void { + if (!IS_SYNTAX_REGEX.test(decl.prop)) { + return; + } + + if (!decl.parent || decl.parent.type !== 'atrule') { + return; + } + + if (!IS_AT_PROPERTY_REGEX.test(decl.parent.name)) { + return; + } + + const originalValue = decl.value; + const tokens = tokenize({ css: decl.value }); + + let stringValue = ''; + tokens.forEach((token) => { + if (isTokenComment(token)) return; + + if (isTokenWhitespace(token)) { + stringValue += " "; + return; + } + + stringValue += token[1]; + }); + + let serialized = '"'; + + for (const part of stringValue) { + const codePoint = part.codePointAt(0); + if (typeof codePoint === "undefined") { + continue; + } + + switch (codePoint) { + case 0x0000: + serialized += String.fromCodePoint(0xFFFD); + break; + case 0x0022: + case 0x005C: + serialized += ("\\" + String.fromCodePoint(codePoint)); + break; + case 0x0001: + case 0x0002: + case 0x0003: + case 0x0004: + case 0x0005: + case 0x0006: + case 0x0007: + case 0x0008: + case 0x0009: + case 0x000A: + case 0x000B: + case 0x000C: + case 0x000D: + case 0x000E: + case 0x000F: + case 0x0010: + case 0x0011: + case 0x0012: + case 0x0013: + case 0x0014: + case 0x0015: + case 0x0016: + case 0x0017: + case 0x0018: + case 0x0019: + case 0x001A: + case 0x001B: + case 0x001C: + case 0x001D: + case 0x001E: + case 0x001F: + case 0x007F: + serialized += ("\\" + codePoint.toString(16) + " "); + break; + + default: + serialized += String.fromCodePoint(codePoint); + break; + } + } + + serialized += '"'; + + if (originalValue === serialized) { + return; + } + + decl.cloneBefore({ + prop: 'color', + value: serialized, + }); + + if (options.preserve) { + return; + } + + decl.remove(); + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/_import.mjs b/plugins/postcss-syntax-descriptor-syntax-production/test/_import.mjs new file mode 100644 index 0000000000..02b22158cd --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/_import.mjs @@ -0,0 +1,10 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import plugin from '@csstools/postcss-syntax-descriptor-syntax-production'; + +test('import', () => { + plugin(); + assert.ok(plugin.postcss, 'should have "postcss flag"'); + assert.equal(typeof plugin, 'function', 'should return a function'); +}); + diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/_require.cjs b/plugins/postcss-syntax-descriptor-syntax-production/test/_require.cjs new file mode 100644 index 0000000000..8f98620a89 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/_require.cjs @@ -0,0 +1,9 @@ +const assert = require('node:assert/strict'); +const test = require('node:test'); +const plugin = require('@csstools/postcss-syntax-descriptor-syntax-production'); + +test('require', () => { + plugin(); + assert.ok(plugin.postcss, 'should have "postcss flag"'); + assert.equal(typeof plugin, 'function', 'should return a function'); +}); diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/_tape.mjs b/plugins/postcss-syntax-descriptor-syntax-production/test/_tape.mjs new file mode 100644 index 0000000000..a498b2836f --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/_tape.mjs @@ -0,0 +1,23 @@ +import { postcssTape } from '@csstools/postcss-tape'; +import plugin from '@csstools/postcss-syntax-descriptor-syntax-production'; + +postcssTape(plugin)({ + basic: { + message: 'supports basic usage', + }, + 'basic:preserve-true': { + message: 'supports basic usage', + options: { + preserve: true, + }, + }, + 'examples/example': { + message: 'minimal example', + }, + 'examples/example:preserve-true': { + message: 'minimal example', + options: { + preserve: true, + }, + }, +}); diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/basic.css b/plugins/postcss-syntax-descriptor-syntax-production/test/basic.css new file mode 100644 index 0000000000..6657ff29f2 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/basic.css @@ -0,0 +1,37 @@ +@property --color { + inherits: true; + initial-value: black; + syntax: ; +} + +@property --multi-line { + inherits: true; + initial-value: black; + syntax: | + | + ; +} + +@property --box-shadow { + inherits: true; + initial-value: black; + syntax: [ inset? && {2,4} && ? ]# | none; +} + +@property --quoted { + inherits: true; + initial-value: black; + syntax: '"' '"'; +} + +@property --function { + inherits: true; + initial-value: black; + syntax: example(first?, second?, third?); +} + +@property --comments { + inherits: true; + initial-value: black; + syntax: /* a comment */ | | ; +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/basic.expect.css b/plugins/postcss-syntax-descriptor-syntax-production/test/basic.expect.css new file mode 100644 index 0000000000..2dcd62c5ef --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/basic.expect.css @@ -0,0 +1,35 @@ +@property --color { + inherits: true; + initial-value: black; + color: ""; +} + +@property --multi-line { + inherits: true; + initial-value: black; + color: " | | "; +} + +@property --box-shadow { + inherits: true; + initial-value: black; + color: "[ inset? && {2,4} && ? ]# | none"; +} + +@property --quoted { + inherits: true; + initial-value: black; + color: "'\"' '\"'"; +} + +@property --function { + inherits: true; + initial-value: black; + color: "example(first?, second?, third?)"; +} + +@property --comments { + inherits: true; + initial-value: black; + color: " | | "; +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/basic.preserve-true.expect.css b/plugins/postcss-syntax-descriptor-syntax-production/test/basic.preserve-true.expect.css new file mode 100644 index 0000000000..a13c76cdfc --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/basic.preserve-true.expect.css @@ -0,0 +1,43 @@ +@property --color { + inherits: true; + initial-value: black; + color: ""; + syntax: ; +} + +@property --multi-line { + inherits: true; + initial-value: black; + color: " | | "; + syntax: | + | + ; +} + +@property --box-shadow { + inherits: true; + initial-value: black; + color: "[ inset? && {2,4} && ? ]# | none"; + syntax: [ inset? && {2,4} && ? ]# | none; +} + +@property --quoted { + inherits: true; + initial-value: black; + color: "'\"' '\"'"; + syntax: '"' '"'; +} + +@property --function { + inherits: true; + initial-value: black; + color: "example(first?, second?, third?)"; + syntax: example(first?, second?, third?); +} + +@property --comments { + inherits: true; + initial-value: black; + color: " | | "; + syntax: /* a comment */ | | ; +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.css b/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.css new file mode 100644 index 0000000000..4c6ca81d4a --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.css @@ -0,0 +1,5 @@ +@property --color { + inherits: true; + initial-value: black; + syntax: ; +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.expect.css b/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.expect.css new file mode 100644 index 0000000000..6710a1730a --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.expect.css @@ -0,0 +1,5 @@ +@property --color { + inherits: true; + initial-value: black; + color: ""; +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.preserve-true.expect.css b/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.preserve-true.expect.css new file mode 100644 index 0000000000..abc051ccb6 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/test/examples/example.preserve-true.expect.css @@ -0,0 +1,6 @@ +@property --color { + inherits: true; + initial-value: black; + color: ""; + syntax: ; +} diff --git a/plugins/postcss-syntax-descriptor-syntax-production/tsconfig.json b/plugins/postcss-syntax-descriptor-syntax-production/tsconfig.json new file mode 100644 index 0000000000..500af6d266 --- /dev/null +++ b/plugins/postcss-syntax-descriptor-syntax-production/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": ".", + "strict": true + }, + "include": ["./src/**/*"], + "exclude": ["dist"] +} diff --git a/rollup/configs/externals.mjs b/rollup/configs/externals.mjs index 352c46f99e..c2d58b8de7 100644 --- a/rollup/configs/externals.mjs +++ b/rollup/configs/externals.mjs @@ -55,12 +55,14 @@ export const externalsForCLI = [ '@csstools/postcss-oklab-function', '@csstools/postcss-position-area-property', '@csstools/postcss-progressive-custom-properties', + '@csstools/postcss-property-rule-prelude-list', '@csstools/postcss-random-function', '@csstools/postcss-rebase-url', '@csstools/postcss-relative-color-syntax', '@csstools/postcss-scope-pseudo-class', '@csstools/postcss-sign-functions', '@csstools/postcss-stepped-value-functions', + '@csstools/postcss-syntax-descriptor-syntax-production', '@csstools/postcss-system-ui-font-family', '@csstools/postcss-text-decoration-shorthand', '@csstools/postcss-trigonometric-functions', @@ -164,12 +166,14 @@ export const externalsForPlugin = [ '@csstools/postcss-oklab-function', '@csstools/postcss-position-area-property', '@csstools/postcss-progressive-custom-properties', + '@csstools/postcss-property-rule-prelude-list', '@csstools/postcss-random-function', '@csstools/postcss-rebase-url', '@csstools/postcss-relative-color-syntax', '@csstools/postcss-scope-pseudo-class', '@csstools/postcss-sign-functions', '@csstools/postcss-stepped-value-functions', + '@csstools/postcss-syntax-descriptor-syntax-production', '@csstools/postcss-system-ui-font-family', '@csstools/postcss-text-decoration-shorthand', '@csstools/postcss-trigonometric-functions',