From 57462127c79057e0e33bddc8f6d3431df57bb4ac Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 22 Nov 2022 08:52:05 +0100 Subject: [PATCH 1/3] wip --- package-lock.json | 88 ++++ .../.gitignore | 6 + .../postcss-custom-media-import-export/.nvmrc | 1 + .../.tape.cjs | 255 +++++++++++ .../CHANGELOG.md | 5 + .../INSTALL.md | 246 +++++++++++ .../LICENSE.md | 21 + .../README.md | 174 ++++++++ .../docs/README.md | 137 ++++++ .../package.json | 95 +++++ .../src/custom-media-from-root.js | 62 +++ .../src/custom-media-name.js | 47 ++ .../src/get-custom-media-from-imports.js | 110 +++++ .../src/index.js | 46 ++ .../src/media-ast-from-string.js | 134 ++++++ .../src/transform-atrules.js | 25 ++ .../src/transform-media-list.js | 88 ++++ .../src/write-custom-media-to-exports.js | 129 ++++++ .../test/_import.mjs | 6 + .../test/_require.cjs | 6 + .../test/basic.css | 132 ++++++ .../test/basic.expect.css | 117 +++++ .../test/basic.import.expect.css | 0 .../test/basic.preserve.expect.css | 224 ++++++++++ .../test/complex.css | 61 +++ .../test/complex.expect.css | 49 +++ .../test/examples/example.css | 5 + .../test/examples/example.expect.css | 3 + .../test/examples/example.preserve.expect.css | 9 + .../test/export-media.css | 8 + .../test/export-media.js | 12 + .../test/export-media.json | 12 + .../test/export-media.mjs | 10 + .../test/import-css.css | 0 .../test/import-media.css | 2 + .../test/import-media.js | 6 + .../test/import-media.json | 6 + .../test/import.css | 29 ++ .../test/import.empty.expect.css | 29 ++ .../test/import.expect.css | 29 ++ .../test/import.plugin.expect.css | 29 ++ .../.gitignore | 14 + .../.nvmrc | 1 + .../.tape.cjs | 29 ++ .../.tape.mjs | 400 ++++++++++++++++++ .../CHANGELOG.md | 5 + .../INSTALL.md | 246 +++++++++++ .../LICENSE.md | 21 + .../README.md | 207 +++++++++ .../package.json | 82 ++++ .../src/index.ts | 107 +++++ .../lib/get-custom-properties-from-imports.ts | 158 +++++++ .../lib/get-custom-properties-from-root.ts | 63 +++ .../src/lib/is-ignored.ts | 20 + .../src/lib/options.ts | 14 + .../src/lib/transform-properties.ts | 86 ++++ .../src/lib/transform-value-ast.ts | 50 +++ .../lib/write-custom-properties-to-exports.ts | 159 +++++++ .../test/_import.mjs | 17 + .../test/_require.cjs | 13 + .../test/basic.css | 160 +++++++ .../test/basic.expect.css | 183 ++++++++ .../test/basic.import-is-empty.expect.css | 183 ++++++++ .../test/basic.import-override.expect.css | 129 ++++++ .../basic.import-override.inverse.expect.css | 129 ++++++ .../test/basic.import.expect.css | 184 ++++++++ .../test/basic.preserve.expect.css | 129 ++++++ .../test/export-properties.css | 20 + .../test/export-properties.js | 22 + .../test/export-properties.json | 22 + .../test/export-properties.mjs | 20 + .../test/export-properties.scss | 18 + .../test/import-properties-2.cjs | 6 + .../test/import-properties-2.css | 4 + .../test/import-properties-2.js | 6 + .../test/import-properties-2.mjs | 6 + .../test/import-properties.cjs | 6 + .../test/import-properties.css | 4 + .../test/import-properties.js | 6 + .../test/import-properties.json | 8 + .../test/import-properties.mjs | 6 + .../test/import-properties.pcss | 4 + .../test/import.css | 5 + .../test/import.expect.css | 8 + .../test/imported-file.css | 3 + .../tsconfig.json | 10 + .../.gitignore | 6 + .../.nvmrc | 1 + .../.tape.cjs | 210 +++++++++ .../.tape.mjs | 232 ++++++++++ .../CHANGELOG.md | 5 + .../INSTALL.md | 257 +++++++++++ .../LICENSE.md | 21 + .../README.md | 128 ++++++ .../docs/README.md | 109 +++++ .../package.json | 96 +++++ .../src/custom-selectors-from-root.js | 32 ++ .../src/export-to.js | 129 ++++++ .../src/import-from.js | 125 ++++++ .../src/index.js | 77 ++++ .../test/_import.mjs | 6 + .../test/_require.cjs | 6 + .../test/basic-import.css | 3 + ...-import.css-from-multiple-files.expect.css | 5 + .../basic-import.css-from-type.expect.css | 5 + .../test/basic-import.css-from.expect.css | 5 + .../test/basic-import.css.expect.css | 5 + .../test/basic-import.empty.expect.css | 3 + .../test/basic-import.expect.css | 5 + .../test/basic-import.fn-promise.expect.css | 5 + .../test/basic-import.fn.expect.css | 5 + .../test/basic-import.js.expect.css | 5 + .../test/basic-import.json.expect.css | 5 + .../test/basic-import.mjs.expect.css | 5 + ...sic-import.with-polyfill-plugin.expect.css | 3 + .../test/basic.css | 126 ++++++ .../test/basic.expect.css | 126 ++++++ .../test/basic.export-css-to-type.expect.css | 126 ++++++ .../test/basic.export-css-to.expect.css | 126 ++++++ .../test/basic.export-css.expect.css | 126 ++++++ .../test/basic.export-fn-promise.expect.css | 126 ++++++ .../test/basic.export-fn.expect.css | 126 ++++++ .../test/basic.export-js.expect.css | 126 ++++++ .../test/basic.export-json.expect.css | 126 ++++++ .../test/basic.export-mjs.expect.css | 126 ++++++ .../test/basic.export.expect.css | 126 ++++++ .../test/empty.css | 0 .../test/examples/.gitkeep | 0 .../test/export-selectors.css | 22 + .../test/export-selectors.js | 20 + .../test/export-selectors.json | 20 + .../test/export-selectors.mjs | 18 + .../test/import-selectors.css | 2 + .../test/import-selectors.js | 6 + .../test/import-selectors.json | 6 + .../test/import-selectors.mjs | 6 + rollup/presets/package-javascript.mjs | 14 +- 137 files changed, 8418 insertions(+), 7 deletions(-) create mode 100644 plugins/postcss-custom-media-import-export/.gitignore create mode 100644 plugins/postcss-custom-media-import-export/.nvmrc create mode 100644 plugins/postcss-custom-media-import-export/.tape.cjs create mode 100644 plugins/postcss-custom-media-import-export/CHANGELOG.md create mode 100644 plugins/postcss-custom-media-import-export/INSTALL.md create mode 100644 plugins/postcss-custom-media-import-export/LICENSE.md create mode 100644 plugins/postcss-custom-media-import-export/README.md create mode 100644 plugins/postcss-custom-media-import-export/docs/README.md create mode 100644 plugins/postcss-custom-media-import-export/package.json create mode 100644 plugins/postcss-custom-media-import-export/src/custom-media-from-root.js create mode 100644 plugins/postcss-custom-media-import-export/src/custom-media-name.js create mode 100644 plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js create mode 100644 plugins/postcss-custom-media-import-export/src/index.js create mode 100644 plugins/postcss-custom-media-import-export/src/media-ast-from-string.js create mode 100644 plugins/postcss-custom-media-import-export/src/transform-atrules.js create mode 100644 plugins/postcss-custom-media-import-export/src/transform-media-list.js create mode 100644 plugins/postcss-custom-media-import-export/src/write-custom-media-to-exports.js create mode 100644 plugins/postcss-custom-media-import-export/test/_import.mjs create mode 100644 plugins/postcss-custom-media-import-export/test/_require.cjs create mode 100644 plugins/postcss-custom-media-import-export/test/basic.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.import.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/complex.css create mode 100644 plugins/postcss-custom-media-import-export/test/complex.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/examples/example.css create mode 100644 plugins/postcss-custom-media-import-export/test/examples/example.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/export-media.css create mode 100644 plugins/postcss-custom-media-import-export/test/export-media.js create mode 100644 plugins/postcss-custom-media-import-export/test/export-media.json create mode 100644 plugins/postcss-custom-media-import-export/test/export-media.mjs create mode 100644 plugins/postcss-custom-media-import-export/test/import-css.css create mode 100644 plugins/postcss-custom-media-import-export/test/import-media.css create mode 100644 plugins/postcss-custom-media-import-export/test/import-media.js create mode 100644 plugins/postcss-custom-media-import-export/test/import-media.json create mode 100644 plugins/postcss-custom-media-import-export/test/import.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.empty.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.plugin.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/.gitignore create mode 100644 plugins/postcss-custom-properties-import-export/.nvmrc create mode 100644 plugins/postcss-custom-properties-import-export/.tape.cjs create mode 100644 plugins/postcss-custom-properties-import-export/.tape.mjs create mode 100644 plugins/postcss-custom-properties-import-export/CHANGELOG.md create mode 100644 plugins/postcss-custom-properties-import-export/INSTALL.md create mode 100644 plugins/postcss-custom-properties-import-export/LICENSE.md create mode 100644 plugins/postcss-custom-properties-import-export/README.md create mode 100644 plugins/postcss-custom-properties-import-export/package.json create mode 100644 plugins/postcss-custom-properties-import-export/src/index.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/options.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts create mode 100644 plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts create mode 100644 plugins/postcss-custom-properties-import-export/test/_import.mjs create mode 100644 plugins/postcss-custom-properties-import-export/test/_require.cjs create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/export-properties.css create mode 100644 plugins/postcss-custom-properties-import-export/test/export-properties.js create mode 100644 plugins/postcss-custom-properties-import-export/test/export-properties.json create mode 100644 plugins/postcss-custom-properties-import-export/test/export-properties.mjs create mode 100644 plugins/postcss-custom-properties-import-export/test/export-properties.scss create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties-2.cjs create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties-2.css create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties-2.js create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties-2.mjs create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties.cjs create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties.css create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties.js create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties.json create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties.mjs create mode 100644 plugins/postcss-custom-properties-import-export/test/import-properties.pcss create mode 100644 plugins/postcss-custom-properties-import-export/test/import.css create mode 100644 plugins/postcss-custom-properties-import-export/test/import.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/imported-file.css create mode 100644 plugins/postcss-custom-properties-import-export/tsconfig.json create mode 100644 plugins/postcss-custom-selectors-import-export/.gitignore create mode 100644 plugins/postcss-custom-selectors-import-export/.nvmrc create mode 100644 plugins/postcss-custom-selectors-import-export/.tape.cjs create mode 100644 plugins/postcss-custom-selectors-import-export/.tape.mjs create mode 100644 plugins/postcss-custom-selectors-import-export/CHANGELOG.md create mode 100644 plugins/postcss-custom-selectors-import-export/INSTALL.md create mode 100644 plugins/postcss-custom-selectors-import-export/LICENSE.md create mode 100644 plugins/postcss-custom-selectors-import-export/README.md create mode 100644 plugins/postcss-custom-selectors-import-export/docs/README.md create mode 100644 plugins/postcss-custom-selectors-import-export/package.json create mode 100644 plugins/postcss-custom-selectors-import-export/src/custom-selectors-from-root.js create mode 100644 plugins/postcss-custom-selectors-import-export/src/export-to.js create mode 100644 plugins/postcss-custom-selectors-import-export/src/import-from.js create mode 100644 plugins/postcss-custom-selectors-import-export/src/index.js create mode 100644 plugins/postcss-custom-selectors-import-export/test/_import.mjs create mode 100644 plugins/postcss-custom-selectors-import-export/test/_require.cjs create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-css-to-type.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-css-to.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-css.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-fn-promise.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-fn.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-js.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-json.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export-mjs.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic.export.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/empty.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/examples/.gitkeep create mode 100644 plugins/postcss-custom-selectors-import-export/test/export-selectors.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/export-selectors.js create mode 100644 plugins/postcss-custom-selectors-import-export/test/export-selectors.json create mode 100644 plugins/postcss-custom-selectors-import-export/test/export-selectors.mjs create mode 100644 plugins/postcss-custom-selectors-import-export/test/import-selectors.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/import-selectors.js create mode 100644 plugins/postcss-custom-selectors-import-export/test/import-selectors.json create mode 100644 plugins/postcss-custom-selectors-import-export/test/import-selectors.mjs diff --git a/package-lock.json b/package-lock.json index c5371965e..1b9d9ab7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1923,6 +1923,18 @@ "resolved": "plugins/postcss-conditional-values", "link": true }, + "node_modules/@csstools/postcss-custom-media-import-export": { + "resolved": "plugins/postcss-custom-media-import-export", + "link": true + }, + "node_modules/@csstools/postcss-custom-properties-import-export": { + "resolved": "plugins/postcss-custom-properties-import-export", + "link": true + }, + "node_modules/@csstools/postcss-custom-selectors-import-export": { + "resolved": "plugins/postcss-custom-selectors-import-export", + "link": true + }, "node_modules/@csstools/postcss-design-tokens": { "resolved": "plugins/postcss-design-tokens", "link": true @@ -7392,6 +7404,24 @@ "postcss": "^8.4" } }, + "plugins/postcss-custom-media-import-export": { + "name": "@csstools/postcss-custom-media-import-export", + "version": "8.0.2", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, "plugins/postcss-custom-properties": { "version": "13.0.0", "license": "MIT", @@ -7412,6 +7442,27 @@ "postcss": "^8.4" } }, + "plugins/postcss-custom-properties-import-export": { + "name": "@csstools/postcss-custom-properties-import-export", + "version": "12.1.10", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "devDependencies": { + "postcss-import": "^15.0.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, "plugins/postcss-custom-selectors": { "version": "7.0.0", "license": "MIT", @@ -7429,6 +7480,24 @@ "postcss": "^8.4" } }, + "plugins/postcss-custom-selectors-import-export": { + "name": "@csstools/postcss-custom-selectors-import-export", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, "plugins/postcss-design-tokens": { "name": "@csstools/postcss-design-tokens", "version": "1.2.0", @@ -9282,6 +9351,25 @@ "postcss-value-parser": "^4.2.0" } }, + "@csstools/postcss-custom-media-import-export": { + "version": "file:plugins/postcss-custom-media-import-export", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-custom-properties-import-export": { + "version": "file:plugins/postcss-custom-properties-import-export", + "requires": { + "postcss-import": "^15.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-custom-selectors-import-export": { + "version": "file:plugins/postcss-custom-selectors-import-export", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, "@csstools/postcss-design-tokens": { "version": "file:plugins/postcss-design-tokens", "requires": { diff --git a/plugins/postcss-custom-media-import-export/.gitignore b/plugins/postcss-custom-media-import-export/.gitignore new file mode 100644 index 000000000..7172b04f1 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/.gitignore @@ -0,0 +1,6 @@ +node_modules +package-lock.json +yarn.lock +*.result.css +*.result.css.map +dist/* diff --git a/plugins/postcss-custom-media-import-export/.nvmrc b/plugins/postcss-custom-media-import-export/.nvmrc new file mode 100644 index 000000000..f0b10f153 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/.nvmrc @@ -0,0 +1 @@ +v16.13.1 diff --git a/plugins/postcss-custom-media-import-export/.tape.cjs b/plugins/postcss-custom-media-import-export/.tape.cjs new file mode 100644 index 000000000..60fff7ba9 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/.tape.cjs @@ -0,0 +1,255 @@ +const postcssTape = require('../../packages/postcss-tape/dist/index.cjs'); +const plugin = require('postcss-custom-media'); +const fs = require('fs'); + +postcssTape(plugin)({ + 'basic': { + message: 'supports basic usage' + }, + 'basic:preserve': { + message: 'supports { preserve: true } usage', + options: { + preserve: true + } + }, + 'examples/example': { + message: 'minimal example', + }, + 'examples/example:preserve': { + message: 'minimal example', + options: { + preserve: true + } + }, + 'complex': { + message: 'supports complex usage' + }, + 'import': { + message: 'supports { importFrom: { customMedia: { ... } } } usage', + options: { + importFrom: { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } + } + } + }, + 'import:import-fn': { + message: 'supports { importFrom() } usage', + options: { + importFrom() { + return { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } + }; + } + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:import-fn-promise': { + message: 'supports { async importFrom() } usage', + options: { + importFrom() { + return new Promise(resolve => { + resolve({ + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } + }) + }); + } + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:json': { + message: 'supports { importFrom: "test/import-media.json" } usage', + options: { + importFrom: 'test/import-media.json' + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:js': { + message: 'supports { importFrom: "test/import-media.js" } usage', + options: { + importFrom: 'test/import-media.js' + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:css': { + message: 'supports { importFrom: "test/import-media.css" } usage', + options: { + importFrom: 'test/import-media.css' + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:css-from': { + message: 'supports { importFrom: { from: "test/import-media.css" } } usage', + options: { + importFrom: { from: 'test/import-media.css' } + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:css-from-type': { + message: 'supports { importFrom: [ { from: "test/import-media.css", type: "css" } ] } usage', + options: { + importFrom: [{ from: 'test/import-media.css', type: 'css' }] + }, + expect: 'import.expect.css', + result: 'import.result.css' + }, + 'import:empty': { + message: 'supports { importFrom: {} } usage', + options: { + importFrom: {} + } + }, + 'basic:export': { + message: 'supports { exportTo: { customMedia: { ... } } } usage', + options: { + exportTo: (global.__exportMediaObject = global.__exportMediaObject || { + customMedia: null + }) + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + after() { + if (__exportMediaObject.customMedia['--mq-a'] !== '(max-width: 30em), (max-height: 30em)') { + throw new Error('The exportTo function failed'); + } + } + }, + 'basic:export-fn': { + message: 'supports { exportTo() } usage', + options: { + exportTo(customMedia) { + if (customMedia['--mq-a'] !== '(max-width: 30em), (max-height: 30em)') { + throw new Error('The exportTo function failed'); + } + } + }, + expect: 'basic.expect.css', + result: 'basic.result.css' + }, + 'basic:export-fn-promise': { + message: 'supports { async exportTo() } usage', + options: { + exportTo(customMedia) { + return new Promise((resolve, reject) => { + if (customMedia['--mq-a'] !== '(max-width: 30em), (max-height: 30em)') { + reject('The exportTo function failed'); + } else { + resolve(); + } + }); + } + }, + expect: 'basic.expect.css', + result: 'basic.result.css' + }, + 'basic:export-json': { + message: 'supports { exportTo: "test/export-media.json" } usage', + options: { + exportTo: 'test/export-media.json' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + global.__exportMediaString = fs.readFileSync('test/export-media.json', 'utf8'); + }, + after() { + if (global.__exportMediaString !== fs.readFileSync('test/export-media.json', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + } + }, + 'basic:export-js': { + message: 'supports { exportTo: "test/export-media.js" } usage', + options: { + exportTo: 'test/export-media.js' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + global.__exportMediaString = fs.readFileSync('test/export-media.js', 'utf8'); + }, + after() { + if (global.__exportMediaString !== fs.readFileSync('test/export-media.js', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + } + }, + 'basic:export-mjs': { + message: 'supports { exportTo: "test/export-media.mjs" } usage', + options: { + exportTo: 'test/export-media.mjs' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + global.__exportMediaString = fs.readFileSync('test/export-media.mjs', 'utf8'); + }, + after() { + if (global.__exportMediaString !== fs.readFileSync('test/export-media.mjs', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + } + }, + 'basic:export-css': { + message: 'supports { exportTo: "test/export-media.css" } usage', + options: { + exportTo: 'test/export-media.css' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + global.__exportMediaString = fs.readFileSync('test/export-media.css', 'utf8'); + }, + after() { + if (global.__exportMediaString !== fs.readFileSync('test/export-media.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + } + }, + 'basic:export-css-to': { + message: 'supports { exportTo: { to: "test/export-media.css" } } usage', + options: { + exportTo: { to: 'test/export-media.css' } + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + global.__exportMediaString = fs.readFileSync('test/export-media.css', 'utf8'); + }, + after() { + if (global.__exportMediaString !== fs.readFileSync('test/export-media.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + } + }, + 'basic:export-css-to-type': { + message: 'supports { exportTo: { to: "test/export-media.css", type: "css" } } usage', + options: { + exportTo: { to: 'test/export-media.css', type: 'css' } + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + global.__exportMediaString = fs.readFileSync('test/export-media.css', 'utf8'); + }, + after() { + if (global.__exportMediaString !== fs.readFileSync('test/export-media.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + } + } +}); diff --git a/plugins/postcss-custom-media-import-export/CHANGELOG.md b/plugins/postcss-custom-media-import-export/CHANGELOG.md new file mode 100644 index 000000000..bc8b2132c --- /dev/null +++ b/plugins/postcss-custom-media-import-export/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changes to PostCSS Custom Media Import/Export + +### 1.0.0 (Unreleased) + +- Initial version diff --git a/plugins/postcss-custom-media-import-export/INSTALL.md b/plugins/postcss-custom-media-import-export/INSTALL.md new file mode 100644 index 000000000..0f3598f29 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/INSTALL.md @@ -0,0 +1,246 @@ +# Installing PostCSS Custom Media + +[PostCSS Custom Media] runs in all Node environments, with special instructions for: + +- [Node](#node) +- [PostCSS CLI](#postcss-cli) +- [PostCSS Load Config](#postcss-load-config) +- [Webpack](#webpack) +- [Create React App](#create-react-app) +- [Next.js](#nextjs) +- [Gulp](#gulp) +- [Grunt](#grunt) + +## Node + +Add [PostCSS Custom Media] to your project: + +```bash +npm install postcss postcss-custom-media --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssCustomMedia = require('postcss-custom-media'); + +postcss([ + postcssCustomMedia(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +## PostCSS CLI + +Add [PostCSS CLI] to your project: + +```bash +npm install postcss-cli postcss-custom-media --save-dev +``` + +Use [PostCSS Custom Media] in your `postcss.config.js` configuration file: + +```js +const postcssCustomMedia = require('postcss-custom-media'); + +module.exports = { + plugins: [ + postcssCustomMedia(/* pluginOptions */) + ] +} +``` + +## PostCSS Load Config + +If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). + +```bash +npm install postcss-custom-media --save-dev +``` + +`package.json`: + +```json +{ + "postcss": { + "plugins": { + "postcss-custom-media": {} + } + } +} +``` + +`.postcssrc.json`: + +```json +{ + "plugins": { + "postcss-custom-media": {} + } +} +``` + +_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 postcss-custom-media --save-dev +``` + +Use [PostCSS Custom Media] 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: [ + [ + "postcss-custom-media", + { + // Options + }, + ], + ], + }, + }, + }, + ], + }, + ], + }, +}; +``` + +## Create React App + +Add [React App Rewired] and [React App Rewire PostCSS] to your project: + +```bash +npm install react-app-rewired react-app-rewire-postcss postcss-custom-media --save-dev +``` + +Use [React App Rewire PostCSS] and [PostCSS Custom Media] in your +`config-overrides.js` file: + +```js +const reactAppRewirePostcss = require('react-app-rewire-postcss'); +const postcssCustomMedia = require('postcss-custom-media'); + +module.exports = config => reactAppRewirePostcss(config, { + plugins: () => [ + postcssCustomMedia(/* pluginOptions */) + ] +}); +``` + +## 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 postcss-custom-media --save-dev +``` + +Use [PostCSS Custom Media] in your `postcss.config.json` file: + +```json +{ + "plugins": [ + "postcss-custom-media" + ] +} +``` + +```json5 +{ + "plugins": [ + [ + "postcss-custom-media", + { + // Optionally add plugin options + } + ] + ] +} +``` + +## Gulp + +Add [Gulp PostCSS] to your project: + +```bash +npm install gulp-postcss postcss-custom-media --save-dev +``` + +Use [PostCSS Custom Media] in your Gulpfile: + +```js +const postcss = require('gulp-postcss'); +const postcssCustomMedia = require('postcss-custom-media'); + +gulp.task('css', function () { + var plugins = [ + postcssCustomMedia(/* pluginOptions */) + ]; + + return gulp.src('./src/*.css') + .pipe(postcss(plugins)) + .pipe(gulp.dest('.')); +}); +``` + +## Grunt + +Add [Grunt PostCSS] to your project: + +```bash +npm install grunt-postcss postcss-custom-media --save-dev +``` + +Use [PostCSS Custom Media] in your Gruntfile: + +```js +const postcssCustomMedia = require('postcss-custom-media'); + +grunt.loadNpmTasks('grunt-postcss'); + +grunt.initConfig({ + postcss: { + options: { + processors: [ + postcssCustomMedia(/* 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 Custom Media]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media +[React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss +[React App Rewired]: https://github.com/timarney/react-app-rewired +[Next.js]: https://nextjs.org diff --git a/plugins/postcss-custom-media-import-export/LICENSE.md b/plugins/postcss-custom-media-import-export/LICENSE.md new file mode 100644 index 000000000..6d7047088 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/LICENSE.md @@ -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. diff --git a/plugins/postcss-custom-media-import-export/README.md b/plugins/postcss-custom-media-import-export/README.md new file mode 100644 index 000000000..ec8f4499b --- /dev/null +++ b/plugins/postcss-custom-media-import-export/README.md @@ -0,0 +1,174 @@ +# PostCSS Custom Media [PostCSS Logo][PostCSS] + +[npm version][npm-url] [CSS Standard Status][css-url] [Build Status][cli-url] [Discord][discord] + +[PostCSS Custom Media] lets you define `@custom-media` in CSS following the [Custom Media Specification]. + +```pcss +@custom-media --small-viewport (max-width: 30em); + +@media (--small-viewport) { + /* styles for small viewport */ +} + +/* becomes */ + +@media (max-width: 30em) { + /* styles for small viewport */ +} +``` + +## Usage + +Add [PostCSS Custom Media] to your project: + +```bash +npm install postcss postcss-custom-media --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssCustomMedia = require('postcss-custom-media'); + +postcss([ + postcssCustomMedia(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +[PostCSS Custom Media] 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) +- [Create React App](INSTALL.md#create-react-app) +- [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 +postcssCustomMedia({ preserve: true }) +``` + +```pcss +@custom-media --small-viewport (max-width: 30em); + +@media (--small-viewport) { + /* styles for small viewport */ +} + +/* becomes */ + +@custom-media --small-viewport (max-width: 30em); + +@media (max-width: 30em) { + /* styles for small viewport */ +} + +@media (--small-viewport) { + /* styles for small viewport */ +} +``` + + +### importFrom + +The `importFrom` option specifies sources where custom media can be imported +from, which might be CSS, JS, and JSON files, functions, and directly passed +objects. + +```js +postcssCustomMedia({ + importFrom: 'path/to/file.css' // => @custom-selector --small-viewport (max-width: 30em); +}); +``` + +```pcss +@media (max-width: 30em) { + /* styles for small viewport */ +} + +@media (--small-viewport) { + /* styles for small viewport */ +} +``` + +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 media using the `customMedia` or +`custom-media` key. + +```js +postcssCustomMedia({ + importFrom: [ + 'path/to/file.css', + 'and/then/this.js', + 'and/then/that.json', + { + customMedia: { '--small-viewport': '(max-width: 30em)' } + }, + () => { + const customMedia = { '--small-viewport': '(max-width: 30em)' }; + + return { customMedia }; + } + ] +}); +``` + +### exportTo + +The `exportTo` option specifies destinations where custom media can be exported +to, which might be CSS, JS, and JSON files, functions, and directly passed +objects. + +```js +postcssCustomMedia({ + exportTo: 'path/to/file.css' // @custom-media --small-viewport (max-width: 30em); +}); +``` + +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 media using the `customMedia` or +`custom-media` key. + +```js +const cachedObject = { customMedia: {} }; + +postcssCustomMedia({ + exportTo: [ + 'path/to/file.css', // @custom-media --small-viewport (max-width: 30em); + 'and/then/this.js', // module.exports = { customMedia: { '--small-viewport': '(max-width: 30em)' } } + 'and/then/this.mjs', // export const customMedia = { '--small-viewport': '(max-width: 30em)' } } + 'and/then/that.json', // { "custom-media": { "--small-viewport": "(max-width: 30em)" } } + cachedObject, + customMedia => { + customMedia // { '--small-viewport': '(max-width: 30em)' } + } + ] +}); +``` + +See example exports written to [CSS](test/export-media.css), +[JS](test/export-media.js), [MJS](test/export-media.mjs), and +[JSON](test/export-media.json). + +[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test +[css-url]: https://cssdb.org/#custom-media-queries +[discord]: https://discord.gg/bUadyRwkJS +[npm-url]: https://www.npmjs.com/package/postcss-custom-media + +[PostCSS]: https://github.com/postcss/postcss +[PostCSS Custom Media]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media +[Custom Media Specification]: https://www.w3.org/TR/mediaqueries-5/#at-ruledef-custom-media diff --git a/plugins/postcss-custom-media-import-export/docs/README.md b/plugins/postcss-custom-media-import-export/docs/README.md new file mode 100644 index 000000000..9961d39fd --- /dev/null +++ b/plugins/postcss-custom-media-import-export/docs/README.md @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + +
+ +[] lets you define `@custom-media` in CSS following the [Custom Media Specification]. + +```pcss + + +/* becomes */ + + +``` + + + + + +## Options + +### preserve + +The `preserve` option determines whether the original notation +is preserved. By default, it is not preserved. + +```js +({ preserve: true }) +``` + +```pcss + + +/* becomes */ + + +``` + + +### importFrom + +The `importFrom` option specifies sources where custom media can be imported +from, which might be CSS, JS, and JSON files, functions, and directly passed +objects. + +```js +({ + importFrom: 'path/to/file.css' // => @custom-selector --small-viewport (max-width: 30em); +}); +``` + +```pcss +@media (max-width: 30em) { + /* styles for small viewport */ +} + +@media (--small-viewport) { + /* styles for small viewport */ +} +``` + +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 media using the `customMedia` or +`custom-media` key. + +```js +({ + importFrom: [ + 'path/to/file.css', + 'and/then/this.js', + 'and/then/that.json', + { + customMedia: { '--small-viewport': '(max-width: 30em)' } + }, + () => { + const customMedia = { '--small-viewport': '(max-width: 30em)' }; + + return { customMedia }; + } + ] +}); +``` + +### exportTo + +The `exportTo` option specifies destinations where custom media can be exported +to, which might be CSS, JS, and JSON files, functions, and directly passed +objects. + +```js +({ + exportTo: 'path/to/file.css' // @custom-media --small-viewport (max-width: 30em); +}); +``` + +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 media using the `customMedia` or +`custom-media` key. + +```js +const cachedObject = { customMedia: {} }; + +({ + exportTo: [ + 'path/to/file.css', // @custom-media --small-viewport (max-width: 30em); + 'and/then/this.js', // module.exports = { customMedia: { '--small-viewport': '(max-width: 30em)' } } + 'and/then/this.mjs', // export const customMedia = { '--small-viewport': '(max-width: 30em)' } } + 'and/then/that.json', // { "custom-media": { "--small-viewport": "(max-width: 30em)" } } + cachedObject, + customMedia => { + customMedia // { '--small-viewport': '(max-width: 30em)' } + } + ] +}); +``` + +See example exports written to [CSS](test/export-media.css), +[JS](test/export-media.js), [MJS](test/export-media.mjs), and +[JSON](test/export-media.json). + + +[Custom Media Specification]: diff --git a/plugins/postcss-custom-media-import-export/package.json b/plugins/postcss-custom-media-import-export/package.json new file mode 100644 index 000000000..cdc1acee4 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/package.json @@ -0,0 +1,95 @@ +{ + "name": "@csstools/postcss-custom-media-import-export", + "description": "Use Custom Media Queries in CSS", + "version": "8.0.2", + "contributors": [ + { + "name": "Antonio Laguna", + "email": "antonio@laguna.es", + "url": "https://antonio.laguna.es" + }, + { + "name": "Romain Menke", + "email": "romainmenke@gmail.com" + }, + { + "name": "Jonathan Neal", + "email": "jonathantneal@hotmail.com" + }, + { + "name": "Maxime Thirouin" + } + ], + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs", + "default": "./dist/index.mjs" + } + }, + "files": [ + "CHANGELOG.md", + "LICENSE.md", + "README.md", + "dist" + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2" + }, + "scripts": { + "build": "rollup -c ../../rollup/default.js", + "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", + "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", + "lint": "npm run lint:eslint && npm run lint:package-json", + "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", + "test": "node .tape.cjs && npm run test:exports", + "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs", + "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.cjs" + }, + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media#readme", + "repository": { + "type": "git", + "url": "https://github.com/csstools/postcss-plugins.git", + "directory": "plugins/postcss-custom-media" + }, + "bugs": "https://github.com/csstools/postcss-plugins/issues", + "keywords": [ + "at-rule", + "atrule", + "css", + "csswg", + "custom", + "media", + "postcss", + "postcss-plugin", + "queries", + "query", + "specification", + "w3c" + ], + "csstools": { + "cssdbId": "custom-media-queries", + "exportName": "postcssCustomMedia", + "humanReadableName": "PostCSS Custom Media", + "specUrl": "https://www.w3.org/TR/mediaqueries-5/#at-ruledef-custom-media" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js b/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js new file mode 100644 index 000000000..d2ddbef6a --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js @@ -0,0 +1,62 @@ +import mediaASTFromString from './media-ast-from-string'; +import valueParser from 'postcss-value-parser'; + +// return custom selectors from the css root, conditionally removing them +export default (root, opts) => { + // initialize custom selectors + const customMedias = {}; + + // for each custom selector atrule that is a child of the css root + root.nodes.slice().forEach(node => { + if (node.type !== 'atrule') { + return; + } + + if (node.name.toLowerCase() !== 'custom-media') { + return; + } + + let paramsAst = null; + try { + paramsAst = valueParser(node.params); + } catch (_) { + return; + } + + if (!paramsAst || !paramsAst.nodes || !paramsAst.nodes.length) { + return; + } + + let nameNodeIndex = -1; + for (let i = 0; i < paramsAst.nodes.length; i++) { + const node = paramsAst.nodes[i]; + if (node.type === 'space' || node.type === 'comment') { + continue; + } + + if (node.type === 'word' && node.value.startsWith('--')) { + nameNodeIndex = i; + break; + } + + return; /* invalid starting node */ + } + + if (nameNodeIndex < 0) { + return; + } + + const name = paramsAst.nodes[nameNodeIndex].value.trim(); + const selectors = valueParser.stringify(paramsAst.nodes.slice(nameNodeIndex + 1)).trim(); + + // write the parsed selectors to the custom selector + customMedias[name] = mediaASTFromString(selectors); + + // conditionally remove the custom selector atrule + if (!Object(opts).preserve) { + node.remove(); + } + }); + + return customMedias; +}; diff --git a/plugins/postcss-custom-media-import-export/src/custom-media-name.js b/plugins/postcss-custom-media-import-export/src/custom-media-name.js new file mode 100644 index 000000000..f9a7aaa5f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/custom-media-name.js @@ -0,0 +1,47 @@ +import valueParser from 'postcss-value-parser'; + +export function getCustomMediaNameReference(source) { + if (!source) { + return; + } + + let paramsAst = null; + try { + paramsAst = valueParser(source); + } catch (_) { + return; + } + + if (!paramsAst || !paramsAst.nodes || !paramsAst.nodes.length) { + return; + } + + if (paramsAst.nodes.length !== 1) { + return; + } + + while (paramsAst.nodes[0].type === 'function' && paramsAst.nodes[0].value === '') { + paramsAst = paramsAst.nodes[0]; + } + + let nameNodeIndex = -1; + for (let i = 0; i < paramsAst.nodes.length; i++) { + const node = paramsAst.nodes[i]; + if (node.type === 'space' || node.type === 'comment') { + continue; + } + + if (node.type === 'word' && node.value.startsWith('--')) { + nameNodeIndex = i; + break; + } + + return; /* invalid starting node */ + } + + if (nameNodeIndex < 0) { + return; + } + + return paramsAst.nodes[nameNodeIndex].value.trim(); +} diff --git a/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js b/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js new file mode 100644 index 000000000..944795ac5 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js @@ -0,0 +1,110 @@ +import fs from 'fs'; +import path from 'path'; +import { parse } from 'postcss'; +import getMediaAstFromMediaString from './media-ast-from-string'; +import getCustomMedia from './custom-media-from-root'; + +/* Get Custom Media from CSS File +/* ========================================================================== */ + +async function getCustomMediaFromCSSFile(from) { + const css = await readFile(from); + const root = parse(css, { from }); + + return getCustomMedia(root, { preserve: true }); +} + +/* Get Custom Media from Object +/* ========================================================================== */ + +function getCustomMediaFromObject(object) { + const customMedia = Object.assign( + {}, + Object(object).customMedia, + Object(object)['custom-media'], + ); + + for (const key in customMedia) { + customMedia[key] = getMediaAstFromMediaString(customMedia[key]); + } + + return customMedia; +} + +/* Get Custom Media from JSON file +/* ========================================================================== */ + +async function getCustomMediaFromJSONFile(from) { + const object = await readJSON(from); + + return getCustomMediaFromObject(object); +} + +/* Get Custom Media from JS file +/* ========================================================================== */ + +async function getCustomMediaFromJSFile(from) { + const object = await import(from); + + return getCustomMediaFromObject(object); +} + +/* Get Custom Media from Sources +/* ========================================================================== */ + +export default function getCustomMediaFromSources(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 media + if (Object(opts).customMedia || Object(opts)['custom-media']) { + return opts; + } + + // source pathname + const from = path.resolve(String(opts.from || '')); + + // type of file being read from + const type = (opts.type || path.extname(from).slice(1)).toLowerCase(); + + return { type, from }; + }).reduce(async (customMedia, source) => { + const { type, from } = await source; + + if (type === 'css' || type === 'pcss') { + return Object.assign(await customMedia, await getCustomMediaFromCSSFile(from)); + } + + if (type === 'js') { + return Object.assign(await customMedia, await getCustomMediaFromJSFile(from)); + } + + if (type === 'json') { + return Object.assign(await customMedia, await getCustomMediaFromJSONFile(from)); + } + + return Object.assign(await customMedia, getCustomMediaFromObject(await source)); + }, {}); +} + +/* 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)); diff --git a/plugins/postcss-custom-media-import-export/src/index.js b/plugins/postcss-custom-media-import-export/src/index.js new file mode 100644 index 000000000..17d08a2ed --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/index.js @@ -0,0 +1,46 @@ +import getCustomMediaFromRoot from './custom-media-from-root'; +import getCustomMediaFromImports from './get-custom-media-from-imports'; +import transformAtrules from './transform-atrules'; +import writeCustomMediaToExports from './write-custom-media-to-exports'; + +const creator = opts => { + // whether to preserve custom media and at-rules using them + const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : false; + + // sources to import custom media from + const importFrom = [].concat(Object(opts).importFrom || []); + + + // destinations to export custom media to + const exportTo = [].concat(Object(opts).exportTo || []); + + // promise any custom media are imported + const customMediaImportsPromise = getCustomMediaFromImports(importFrom); + + const customMediaHelperKey = Symbol('customMediaHelper'); + + return { + postcssPlugin: 'postcss-custom-media', + Once: async (root, helpers) => { + + // combine rules from root and from imports + helpers[customMediaHelperKey] = Object.assign( + await customMediaImportsPromise, + getCustomMediaFromRoot(root, { preserve }), + ); + + await writeCustomMediaToExports(helpers[customMediaHelperKey], exportTo); + }, + AtRule: (atrule, helpers) => { + if (atrule.name !== 'media') { + return; + } + + transformAtrules(atrule, helpers[customMediaHelperKey], { preserve }); + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-custom-media-import-export/src/media-ast-from-string.js b/plugins/postcss-custom-media-import-export/src/media-ast-from-string.js new file mode 100644 index 000000000..3821a319c --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/media-ast-from-string.js @@ -0,0 +1,134 @@ +function parse(string, splitByAnd) { + const array = []; + let buffer = ''; + let split = false; + let func = 0; + let i = -1; + + while (++i < string.length) { + const char = string[i]; + + if (char === '(') { + func += 1; + } else if (char === ')') { + if (func > 0) { + func -= 1; + } + } else if (func === 0) { + if (splitByAnd && andRegExp.test(buffer + char)) { + split = true; + } else if (!splitByAnd && char === ',') { + split = true; + } + } + + if (split) { + array.push(splitByAnd ? new MediaExpression(buffer + char) : new MediaQuery(buffer)); + + buffer = ''; + split = false; + } else { + buffer += char; + } + } + + if (buffer !== '') { + array.push(splitByAnd ? new MediaExpression(buffer) : new MediaQuery(buffer)); + } + + return array; +} + +class MediaQueryList { + constructor(string) { + this.nodes = parse(string); + } + + invert() { + this.nodes.forEach(node => { + node.invert(); + }); + + return this; + } + + clone() { + return new MediaQueryList(String(this)); + } + + toString() { + return this.nodes.join(','); + } +} + +class MediaQuery { + constructor(string) { + const [, before, media, after ] = string.match(spaceWrapRegExp); + const [, modifier = '', afterModifier = ' ', type = '', beforeAnd = '', and = '', beforeExpression = '', expression1 = '', expression2 = ''] = media.match(mediaRegExp) || []; + const raws = { before, after, afterModifier, originalModifier: modifier || '', beforeAnd, and, beforeExpression }; + const nodes = parse(expression1 || expression2, true); + + Object.assign(this, { + modifier, + type, + raws, + nodes, + }); + } + + clone(overrides) { + const instance = new MediaQuery(String(this)); + + Object.assign(instance, overrides); + + return instance; + } + + invert() { + this.modifier = this.modifier ? '' : this.raws.originalModifier; + + return this; + } + + toString() { + const { raws } = this; + + return `${raws.before}${this.modifier}${this.modifier ? `${raws.afterModifier}` : ''}${this.type}${raws.beforeAnd}${raws.and}${raws.beforeExpression}${this.nodes.join('')}${this.raws.after}`; + } +} + +class MediaExpression { + constructor(string) { + const [, value, after = '', and = '', afterAnd = '' ] = string.match(andRegExp) || [null, string]; + const raws = { after, and, afterAnd }; + + Object.assign(this, { value, raws }); + } + + clone(overrides) { + const instance = new MediaExpression(String(this)); + + Object.assign(instance, overrides); + + return instance; + } + + toString() { + const { raws } = this; + + return `${this.value}${raws.after}${raws.and}${raws.afterAnd}`; + } +} + +const modifierRE = '(not|only)'; +const typeRE = '(all|print|screen|speech)'; +const noExpressionRE = '([\\W\\w]*)'; +const expressionRE = '([\\W\\w]+)'; +const noSpaceRE = '(\\s*)'; +const spaceRE = '(\\s+)'; +const andRE = '(?:(\\s+)(and))'; +const andRegExp = new RegExp(`^${expressionRE}(?:${andRE}${spaceRE})$`, 'i'); +const spaceWrapRegExp = new RegExp(`^${noSpaceRE}${noExpressionRE}${noSpaceRE}$`); +const mediaRegExp = new RegExp(`^(?:${modifierRE}${spaceRE})?(?:${typeRE}(?:${andRE}${spaceRE}${expressionRE})?|${expressionRE})$`, 'i'); + +export default string => new MediaQueryList(string); diff --git a/plugins/postcss-custom-media-import-export/src/transform-atrules.js b/plugins/postcss-custom-media-import-export/src/transform-atrules.js new file mode 100644 index 000000000..130e0bbb9 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/transform-atrules.js @@ -0,0 +1,25 @@ +import transformMediaList from './transform-media-list'; +import mediaASTFromString from './media-ast-from-string'; + +// transform custom pseudo selectors with custom selectors +export default (atrule, customMedia, { preserve }) => { + if (atrule.params.indexOf('--') > -1) { + const mediaAST = mediaASTFromString(atrule.params); + const params = String(transformMediaList(mediaAST, customMedia)); + if (params === null) { + return; + } + + if (params === atrule.params) { + return; + } + + atrule.cloneBefore({ + params: params, + }); + + if (!preserve) { + atrule.remove(); + } + } +}; diff --git a/plugins/postcss-custom-media-import-export/src/transform-media-list.js b/plugins/postcss-custom-media-import-export/src/transform-media-list.js new file mode 100644 index 000000000..2eed88fdb --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/transform-media-list.js @@ -0,0 +1,88 @@ +import { getCustomMediaNameReference } from './custom-media-name'; + +// return transformed medias, replacing custom pseudo medias with custom medias +export default function transformMediaList(mediaList, customMedias) { + let index = mediaList.nodes.length - 1; + + while (index >= 0) { + const transformedMedias = transformMedia(mediaList.nodes[index], customMedias); + + if (transformedMedias.length) { + mediaList.nodes.splice(index, 1, ...transformedMedias); + } + + --index; + } + + return mediaList; +} + +// return custom pseudo medias replaced with custom medias +function transformMedia(media, customMedias) { + const transpiledMedias = []; + + for (const index in media.nodes) { + const { value, nodes } = media.nodes[index]; + const key = getCustomMediaNameReference(value); + if (key && (key in customMedias)) { + for (const replacementMedia of customMedias[key].nodes) { + // use the first available modifier unless they cancel each other out + const modifier = media.modifier !== replacementMedia.modifier + ? media.modifier || replacementMedia.modifier + : ''; + const mediaClone = media.clone({ + modifier, + // conditionally use the raws from the first available modifier + raws: !modifier || media.modifier + ? { ...media.raws } + : { ...replacementMedia.raws }, + type: media.type || replacementMedia.type, + }); + + // conditionally include more replacement raws when the type is present + if (mediaClone.type === replacementMedia.type) { + Object.assign(mediaClone.raws, { + and: replacementMedia.raws.and, + beforeAnd: replacementMedia.raws.beforeAnd, + beforeExpression: replacementMedia.raws.beforeExpression, + }); + } + + mediaClone.nodes.splice(index, 1, ...replacementMedia.clone().nodes.map(node => { + // use raws and spacing from the current usage + if (media.nodes[index].raws.and) { + node.raws = { ...media.nodes[index].raws }; + } + + node.spaces = { ...media.nodes[index].spaces }; + + return node; + })); + + // remove the currently transformed key to prevent recursion + const nextCustomMedia = getCustomMediasWithoutKey(customMedias, key); + const retranspiledMedias = transformMedia(mediaClone, nextCustomMedia); + + if (retranspiledMedias.length) { + transpiledMedias.push(...retranspiledMedias); + } else { + transpiledMedias.push(mediaClone); + } + } + + return transpiledMedias; + } else if (nodes && nodes.length) { + transformMediaList(media.nodes[index], customMedias); + } + } + + return transpiledMedias; +} + +const getCustomMediasWithoutKey = (customMedias, key) => { + const nextCustomMedias = Object.assign({}, customMedias); + + delete nextCustomMedias[key]; + + return nextCustomMedias; +}; diff --git a/plugins/postcss-custom-media-import-export/src/write-custom-media-to-exports.js b/plugins/postcss-custom-media-import-export/src/write-custom-media-to-exports.js new file mode 100644 index 000000000..52754f3f5 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/src/write-custom-media-to-exports.js @@ -0,0 +1,129 @@ +import fs from 'fs'; +import path from 'path'; + +/* Write Custom Media from CSS File +/* ========================================================================== */ + +async function writeCustomMediaToCssFile(to, customMedia) { + const cssContent = Object.keys(customMedia).reduce((cssLines, name) => { + cssLines.push(`@custom-media ${name} ${customMedia[name]};`); + + return cssLines; + }, []).join('\n'); + const css = `${cssContent}\n`; + + await writeFile(to, css); +} + +/* Write Custom Media from JSON file +/* ========================================================================== */ + +async function writeCustomMediaToJsonFile(to, customMedia) { + const jsonContent = JSON.stringify({ + 'custom-media': customMedia, + }, null, '\t'); + const json = `${jsonContent}\n`; + + await writeFile(to, json); +} + +/* Write Custom Media from Common JS file +/* ========================================================================== */ + +async function writeCustomMediaToCjsFile(to, customMedia) { + const jsContents = Object.keys(customMedia).reduce((jsLines, name) => { + jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(customMedia[name])}'`); + + return jsLines; + }, []).join(',\n'); + const js = `module.exports = {\n\tcustomMedia: {\n${jsContents}\n\t}\n};\n`; + + await writeFile(to, js); +} + +/* Write Custom Media from Module JS file +/* ========================================================================== */ + +async function writeCustomMediaToMjsFile(to, customMedia) { + const mjsContents = Object.keys(customMedia).reduce((mjsLines, name) => { + mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(customMedia[name])}'`); + + return mjsLines; + }, []).join(',\n'); + const mjs = `export const customMedia = {\n${mjsContents}\n};\n`; + + await writeFile(to, mjs); +} + +/* Write Custom Media to Exports +/* ========================================================================== */ + +export default function writeCustomMediaToExports(customMedia, destinations) { + return Promise.all(destinations.map(async destination => { + if (destination instanceof Function) { + await destination(defaultCustomMediaToJSON(customMedia)); + } else { + // read the destination as an object + const opts = destination === Object(destination) ? destination : { to: String(destination) }; + + // transformer for custom media into a JSON-compatible object + const toJSON = opts.toJSON || defaultCustomMediaToJSON; + + if ('customMedia' in opts) { + // write directly to an object as customMedia + opts.customMedia = toJSON(customMedia); + } else if ('custom-media' in opts) { + // write directly to an object as custom-media + opts['custom-media'] = toJSON(customMedia); + } else { + // destination pathname + const to = String(opts.to || ''); + + // type of file being written to + const type = (opts.type || path.extname(to).slice(1)).toLowerCase(); + + // transformed custom media + const customMediaJSON = toJSON(customMedia); + + if (type === 'css') { + await writeCustomMediaToCssFile(to, customMediaJSON); + } + + if (type === 'js') { + await writeCustomMediaToCjsFile(to, customMediaJSON); + } + + if (type === 'json') { + await writeCustomMediaToJsonFile(to, customMediaJSON); + } + + if (type === 'mjs') { + await writeCustomMediaToMjsFile(to, customMediaJSON); + } + } + } + })); +} + +/* Helper utilities +/* ========================================================================== */ + +const defaultCustomMediaToJSON = customMedia => { + return Object.keys(customMedia).reduce((customMediaJSON, key) => { + customMediaJSON[key] = String(customMedia[key]); + + return customMediaJSON; + }, {}); +}; + +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'); diff --git a/plugins/postcss-custom-media-import-export/test/_import.mjs b/plugins/postcss-custom-media-import-export/test/_import.mjs new file mode 100644 index 000000000..e5c94d2ab --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/_import.mjs @@ -0,0 +1,6 @@ +import assert from 'assert'; +import plugin from 'postcss-custom-media'; +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); diff --git a/plugins/postcss-custom-media-import-export/test/_require.cjs b/plugins/postcss-custom-media-import-export/test/_require.cjs new file mode 100644 index 000000000..8c9e7f530 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/_require.cjs @@ -0,0 +1,6 @@ +const assert = require('assert'); +const plugin = require('postcss-custom-media'); +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); diff --git a/plugins/postcss-custom-media-import-export/test/basic.css b/plugins/postcss-custom-media-import-export/test/basic.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.expect.css b/plugins/postcss-custom-media-import-export/test/basic.expect.css new file mode 100644 index 000000000..ed797941c --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.expect.css @@ -0,0 +1,117 @@ +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1; + } +} + +@media screen and (max-width: 30em) { + body { + order: 1; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 2; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 1; + } +} + +@media all and (max-width: 30em),all and (max-height: 30em) { + body { + order: 2; + } +} + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@media (min-width: 320px) and (max-width: 640px) { + body { + order: 6; + } +} + +@media (min-width: 320px) and (max-width: 640px) { + body { + order: 7; + } +} + +@media (min-width: 320px) and (max-width: 640px) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1000; + } +} + +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1001; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1002; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1003; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1004; + } +} + +@media (max-width: 30em),(max-height: 30em), +(max-width: 30em), +(max-height: 30em) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.import.expect.css b/plugins/postcss-custom-media-import-export/test/basic.import.expect.css new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css b/plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css new file mode 100644 index 000000000..4c8aefc63 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css @@ -0,0 +1,224 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1; + } +} + +@media (--mq-a) { + body { + order: 1; + } +} + +@media screen and (max-width: 30em) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 2; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 1; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media all and (max-width: 30em),all and (max-height: 30em) { + body { + order: 2; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (min-width: 320px) and (max-width: 640px) { + body { + order: 6; + } +} + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (min-width: 320px) and (max-width: 640px) { + body { + order: 7; + } +} + +@media (--concat) { + body { + order: 7; + } +} + +@media (min-width: 320px) and (max-width: 640px) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1001; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1004; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media (max-width: 30em),(max-height: 30em), +(max-width: 30em), +(max-height: 30em) { + body { + order: 1005; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/complex.css b/plugins/postcss-custom-media-import-export/test/complex.css new file mode 100644 index 000000000..4b427cbd0 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/complex.css @@ -0,0 +1,61 @@ +@custom-media --🧑🏾‍🎤 (min-width: 1); + +@media (--🧑🏾‍🎤) { + .a { + order: 1; + } +} + +@custom-media --\(\)-escaped (min-width: 2); + +@media (--\(\)-escaped) { + .a { + order: 2; + } +} + +@custom-media --modern (min-width: 3), (min-width: 4); + +@media (--modern) and (width > 1024px) { + .a { order: 3; } +} + +/* #region https://github.com/csstools/postcss-custom-media/issues/51 */ +/* TODO: This is broken at the moment */ +@custom-media --screen only screen; +@custom-media --md-and-larger1 --screen and (width >= 570px); +@custom-media --md-and-larger2 (--screen) and (width >= 570px); +@custom-media --md-and-larger3 only screen and (width >= 570px); +@custom-media --md-larger4 (width >=570px); +@custom-media --md-smaller4 (width < 1000px); + +@media (--md-and-larger1) { + body { + background-color: red; + } +} + +@media (--md-and-larger2) { + body { + background-color: yellow; + } +} + +@media (--md-and-larger3) { + body { + background-color: green; + } +} + +@media (--screen) and (--md-larger4) { + body { + background-color: green; + } +} + +@media (--md-larger4) and (--md-smaller4) { + body { + background-color: green; + } +} +/* #endregion https://github.com/csstools/postcss-custom-media/issues/51 */ diff --git a/plugins/postcss-custom-media-import-export/test/complex.expect.css b/plugins/postcss-custom-media-import-export/test/complex.expect.css new file mode 100644 index 000000000..486d7f515 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/complex.expect.css @@ -0,0 +1,49 @@ +@media (min-width: 1) { + .a { + order: 1; + } +} + +@media (min-width: 2) { + .a { + order: 2; + } +} + +@media (min-width: 3) and (width > 1024px),(min-width: 4) and (width > 1024px) { + .a { order: 3; } +} + +/* #region https://github.com/csstools/postcss-custom-media/issues/51 */ +/* TODO: This is broken at the moment */ + +@media only screen(width >= 570px) { + body { + background-color: red; + } +} + +@media only screen(width >= 570px) { + body { + background-color: yellow; + } +} + +@media only screen and (width >= 570px) { + body { + background-color: green; + } +} + +@media only screen(width >=570px) { + body { + background-color: green; + } +} + +@media (width >=570px) and (width < 1000px) { + body { + background-color: green; + } +} +/* #endregion https://github.com/csstools/postcss-custom-media/issues/51 */ diff --git a/plugins/postcss-custom-media-import-export/test/examples/example.css b/plugins/postcss-custom-media-import-export/test/examples/example.css new file mode 100644 index 000000000..fe7634abb --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/examples/example.css @@ -0,0 +1,5 @@ +@custom-media --small-viewport (max-width: 30em); + +@media (--small-viewport) { + /* styles for small viewport */ +} diff --git a/plugins/postcss-custom-media-import-export/test/examples/example.expect.css b/plugins/postcss-custom-media-import-export/test/examples/example.expect.css new file mode 100644 index 000000000..3b68ef9be --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/examples/example.expect.css @@ -0,0 +1,3 @@ +@media (max-width: 30em) { + /* styles for small viewport */ +} diff --git a/plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css b/plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css new file mode 100644 index 000000000..ed814500b --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css @@ -0,0 +1,9 @@ +@custom-media --small-viewport (max-width: 30em); + +@media (max-width: 30em) { + /* styles for small viewport */ +} + +@media (--small-viewport) { + /* styles for small viewport */ +} diff --git a/plugins/postcss-custom-media-import-export/test/export-media.css b/plugins/postcss-custom-media-import-export/test/export-media.css new file mode 100644 index 000000000..f51e88c82 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/export-media.css @@ -0,0 +1,8 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); +@custom-media --concat (min-width: 320px) and (max-width: 640px); diff --git a/plugins/postcss-custom-media-import-export/test/export-media.js b/plugins/postcss-custom-media-import-export/test/export-media.js new file mode 100644 index 000000000..acccd8408 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/export-media.js @@ -0,0 +1,12 @@ +module.exports = { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--mq-b': 'screen and (max-width: 30em)', + '--not-mq-a': 'not all and (--mq-a)', + '--circular-mq-a': '(--circular-mq-b)', + '--circular-mq-b': '(--circular-mq-a)', + '--min': '(min-width: 320px)', + '--max': '(max-width: 640px)', + '--concat': '(min-width: 320px) and (max-width: 640px)' + } +}; diff --git a/plugins/postcss-custom-media-import-export/test/export-media.json b/plugins/postcss-custom-media-import-export/test/export-media.json new file mode 100644 index 000000000..729bde28e --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/export-media.json @@ -0,0 +1,12 @@ +{ + "custom-media": { + "--mq-a": "(max-width: 30em), (max-height: 30em)", + "--mq-b": "screen and (max-width: 30em)", + "--not-mq-a": "not all and (--mq-a)", + "--circular-mq-a": "(--circular-mq-b)", + "--circular-mq-b": "(--circular-mq-a)", + "--min": "(min-width: 320px)", + "--max": "(max-width: 640px)", + "--concat": "(min-width: 320px) and (max-width: 640px)" + } +} diff --git a/plugins/postcss-custom-media-import-export/test/export-media.mjs b/plugins/postcss-custom-media-import-export/test/export-media.mjs new file mode 100644 index 000000000..ea36519d8 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/export-media.mjs @@ -0,0 +1,10 @@ +export const customMedia = { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--mq-b': 'screen and (max-width: 30em)', + '--not-mq-a': 'not all and (--mq-a)', + '--circular-mq-a': '(--circular-mq-b)', + '--circular-mq-b': '(--circular-mq-a)', + '--min': '(min-width: 320px)', + '--max': '(max-width: 640px)', + '--concat': '(min-width: 320px) and (max-width: 640px)' +}; diff --git a/plugins/postcss-custom-media-import-export/test/import-css.css b/plugins/postcss-custom-media-import-export/test/import-css.css new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/postcss-custom-media-import-export/test/import-media.css b/plugins/postcss-custom-media-import-export/test/import-media.css new file mode 100644 index 000000000..e788f32ae --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import-media.css @@ -0,0 +1,2 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --not-mq-a not all and (--mq-a); diff --git a/plugins/postcss-custom-media-import-export/test/import-media.js b/plugins/postcss-custom-media-import-export/test/import-media.js new file mode 100644 index 000000000..3f2e0401a --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import-media.js @@ -0,0 +1,6 @@ +module.exports = { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import-media.json b/plugins/postcss-custom-media-import-export/test/import-media.json new file mode 100644 index 000000000..807d8dfdd --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import-media.json @@ -0,0 +1,6 @@ +{ + "customMedia": { + "--mq-a": "(max-width: 30em), (max-height: 30em)", + "--not-mq-a": "not all and (--mq-a)" + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.css b/plugins/postcss-custom-media-import-export/test/import.css new file mode 100644 index 000000000..f37220e74 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.css @@ -0,0 +1,29 @@ +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.empty.expect.css b/plugins/postcss-custom-media-import-export/test/import.empty.expect.css new file mode 100644 index 000000000..f37220e74 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.empty.expect.css @@ -0,0 +1,29 @@ +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.expect.css b/plugins/postcss-custom-media-import-export/test/import.expect.css new file mode 100644 index 000000000..0bc2bbf3b --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.expect.css @@ -0,0 +1,29 @@ +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 2; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 1; + } +} + +@media all and (max-width: 30em),all and (max-height: 30em) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.plugin.expect.css b/plugins/postcss-custom-media-import-export/test/import.plugin.expect.css new file mode 100644 index 000000000..0bc2bbf3b --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.plugin.expect.css @@ -0,0 +1,29 @@ +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1; + } +} + +@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { + body { + order: 1; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 2; + } +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + body { + order: 1; + } +} + +@media all and (max-width: 30em),all and (max-height: 30em) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-properties-import-export/.gitignore b/plugins/postcss-custom-properties-import-export/.gitignore new file mode 100644 index 000000000..e5835f4f9 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/.gitignore @@ -0,0 +1,14 @@ +node_modules +dist +package-lock.json +yarn.lock +browser.js +browser.min.js +*.log* +*.result.css +*.result.css.map +!.editorconfig +!.gitignore +!.rollup.js +!.tape.js +!.travis.yml diff --git a/plugins/postcss-custom-properties-import-export/.nvmrc b/plugins/postcss-custom-properties-import-export/.nvmrc new file mode 100644 index 000000000..f0b10f153 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/.nvmrc @@ -0,0 +1 @@ +v16.13.1 diff --git a/plugins/postcss-custom-properties-import-export/.tape.cjs b/plugins/postcss-custom-properties-import-export/.tape.cjs new file mode 100644 index 000000000..3c717ab09 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/.tape.cjs @@ -0,0 +1,29 @@ +const postcssTape = require('../../packages/postcss-tape/dist/index.cjs'); +const plugin = require('postcss-custom-properties'); + +postcssTape(plugin)({ + 'basic:import-cjs': { + message: 'supports { importFrom: "test/import-properties{-2}?.cjs" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.cjs', + 'test/import-properties-2.cjs' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-css-js': { + message: 'supports { importFrom: "test/import-properties{-2}?.{css|js}" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.js', + 'test/import-properties-2.css' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + } +}); diff --git a/plugins/postcss-custom-properties-import-export/.tape.mjs b/plugins/postcss-custom-properties-import-export/.tape.mjs new file mode 100644 index 000000000..43fa4ae48 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/.tape.mjs @@ -0,0 +1,400 @@ +import postcssTape from '../../packages/postcss-tape/dist/index.mjs'; +import plugin from 'postcss-custom-properties'; +import { strict as assert } from 'assert'; +import postcssImport from 'postcss-import'; +import fs from 'fs'; + +postcssTape(plugin)({ + 'basic': { + message: 'supports basic usage' + }, + 'basic:preserve': { + message: 'supports { preserve: false } usage', + options: { + preserve: false + } + }, + 'basic:import': { + message: 'supports { importFrom: { customProperties: { ... } } } usage', + warnings: 1, + options: { + importFrom: { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--z-index': 10 + } + } + } + }, + 'basic:import-fn': { + message: 'supports { importFrom() } usage', + warnings: 1, + options: { + importFrom() { + return { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--z-index': 10 + } + }; + } + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-fn-promise': { + message: 'supports { async importFrom() } usage', + warnings: 1, + options: { + importFrom() { + return new Promise(resolve => { + resolve({ + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--z-index': 10 + } + }) + }); + } + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-json': { + message: 'supports { importFrom: "test/import-properties.json" } usage', + warnings: 1, + options: { + importFrom: 'test/import-properties.json' + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-cjs': { + message: 'supports { importFrom: "test/import-properties{-2}?.cjs" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.cjs', + 'test/import-properties-2.cjs' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-mjs': { + message: 'supports { importFrom: "test/import-properties{-2}?.mjs" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.mjs', + 'test/import-properties-2.mjs' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-css': { + message: 'supports { importFrom: "test/import-properties{-2}?.css" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.css', + 'test/import-properties-2.css' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-css-js': { + message: 'supports { importFrom: "test/import-properties{-2}?.{css|js}" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.js', + 'test/import-properties-2.css' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-css-pcss': { + message: 'supports { importFrom: "test/import-properties.{p}?css" } usage', + warnings: 1, + options: { + importFrom: [ + 'test/import-properties.pcss', + 'test/import-properties-2.css' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-css-from': { + message: 'supports { importFrom: { from: "test/import-properties.css" } } usage', + warnings: 1, + options: { + importFrom: [ + { from: 'test/import-properties.css' }, + { from: 'test/import-properties-2.css' } + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-css-from-type': { + message: 'supports { importFrom: [ { from: "test/import-properties.css", type: "css" } ] } usage', + warnings: 1, + options: { + importFrom: [ + { from: 'test/import-properties.css', type: 'css' }, + { from: 'test/import-properties-2.css', type: 'css' } + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, + 'basic:import-override': { + message: 'importFrom with { preserve: false } should override root properties', + warnings: 1, + options: { + preserve: false, + importFrom: { + customProperties: { + '--color': 'rgb(0, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--shadow-color': 'rgb(0,0,0)', + '--z-index': 10 + } + } + }, + expect: 'basic.import-override.expect.css', + result: 'basic.import-override.result.css' + }, + 'basic:import-override:inverse': { + message: 'importFrom with { preserve: false, overrideImportFromWithRoot: true } should override importFrom properties', + warnings: 1, + options: { + preserve: false, + overrideImportFromWithRoot: true, + importFrom: { + customProperties: { + '--color': 'rgb(0, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--shadow-color': 'rgb(0,0,0)', + '--z-index': 10 + } + } + }, + expect: 'basic.import-override.inverse.expect.css', + result: 'basic.import-override.inverse.result.css' + }, + 'basic:export': { + message: 'supports { exportTo: { customProperties: { ... } } } usage', + warnings: 1, + options: { + exportTo: (global.__exportPropertiesObject = global.__exportPropertiesObject || { + customProperties: null + }) + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + after() { + if (__exportPropertiesObject.customProperties['--color'] !== 'rgb(255, 0, 0)') { + throw new Error('The exportTo function failed'); + } + } + }, + 'basic:export-fn': { + message: 'supports { exportTo() } usage', + warnings: 1, + options: { + exportTo(customProperties) { + if (customProperties['--color'] !== 'rgb(255, 0, 0)') { + throw new Error('The exportTo function failed'); + } + } + }, + expect: 'basic.expect.css', + result: 'basic.result.css' + }, + 'basic:export-fn-promise': { + message: 'supports { async exportTo() } usage', + warnings: 1, + options: { + exportTo(customProperties) { + return new Promise((resolve, reject) => { + if (customProperties['--color'] !== 'rgb(255, 0, 0)') { + reject('The exportTo function failed'); + } else { + resolve(); + } + }); + } + }, + expect: 'basic.expect.css', + result: 'basic.result.css' + }, + 'basic:export-scss': { + message: 'supports { exportTo: "test/export-properties.scss" } usage', + warnings: 1, + options: { + exportTo: 'test/export-properties.scss' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.scss', 'utf8'); + fs.rmSync('test/export-properties.scss'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.scss', 'utf8')); + } + }, + 'basic:export-json': { + message: 'supports { exportTo: "test/export-properties.json" } usage', + warnings: 1, + options: { + exportTo: 'test/export-properties.json' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.json', 'utf8'); + fs.rmSync('test/export-properties.json'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.json', 'utf8')); + } + }, + 'basic:export-js': { + message: 'supports { exportTo: "test/export-properties.js" } usage', + warnings: 1, + options: { + exportTo: 'test/export-properties.js' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.js', 'utf8'); + fs.rmSync('test/export-properties.js'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.js', 'utf8')); + } + }, + 'basic:export-mjs': { + message: 'supports { exportTo: "test/export-properties.mjs" } usage', + warnings: 1, + options: { + exportTo: 'test/export-properties.mjs' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.mjs', 'utf8'); + fs.rmSync('test/export-properties.mjs'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.mjs', 'utf8')); + } + }, + 'basic:export-css': { + message: 'supports { exportTo: "test/export-properties.css" } usage', + warnings: 1, + options: { + exportTo: 'test/export-properties.css' + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.css', 'utf8'); + fs.rmSync('test/export-properties.css'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.css', 'utf8')); + } + }, + 'basic:export-css-to': { + message: 'supports { exportTo: { to: "test/export-properties.css" } } usage', + warnings: 1, + options: { + exportTo: { to: 'test/export-properties.css' } + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.css', 'utf8'); + fs.rmSync('test/export-properties.css'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.css', 'utf8')); + } + }, + 'basic:export-css-to-type': { + message: 'supports { exportTo: { to: "test/export-properties.css", type: "css" } } usage', + warnings: 1, + options: { + exportTo: { to: 'test/export-properties.css', type: 'css' } + }, + expect: 'basic.expect.css', + result: 'basic.result.css', + before() { + try { + global.__exportPropertiesString = fs.readFileSync('test/export-properties.css', 'utf8'); + fs.rmSync('test/export-properties.css'); + } catch (_) { + // ignore + } + }, + after() { + assert.strictEqual(global.__exportPropertiesString, fs.readFileSync('test/export-properties.css', 'utf8')); + } + }, + 'basic:import-is-empty': { + message: 'supports { importFrom: {} } usage', + options: { + importFrom: {}, + disableDeprecationNotice: true + } + }, + 'import': { + message: 'supports "postcss-import"', + plugins: [postcssImport(), plugin()] + } +}); diff --git a/plugins/postcss-custom-properties-import-export/CHANGELOG.md b/plugins/postcss-custom-properties-import-export/CHANGELOG.md new file mode 100644 index 000000000..df73cd762 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changes to PostCSS Custom Properties Import/Export + +### 1.0.0 (Unreleased) + +- Initial version diff --git a/plugins/postcss-custom-properties-import-export/INSTALL.md b/plugins/postcss-custom-properties-import-export/INSTALL.md new file mode 100644 index 000000000..17e15cb65 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/INSTALL.md @@ -0,0 +1,246 @@ +# Installing PostCSS Custom Properties + +[PostCSS Custom Properties] runs in all Node environments, with special instructions for: + +- [Node](#node) +- [PostCSS CLI](#postcss-cli) +- [PostCSS Load Config](#postcss-load-config) +- [Webpack](#webpack) +- [Create React App](#create-react-app) +- [Next.js](#nextjs) +- [Gulp](#gulp) +- [Grunt](#grunt) + +## Node + +Add [PostCSS Custom Properties] to your project: + +```bash +npm install postcss postcss-custom-properties --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssCustomProperties = require('postcss-custom-properties'); + +postcss([ + postcssCustomProperties(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +## PostCSS CLI + +Add [PostCSS CLI] to your project: + +```bash +npm install postcss-cli postcss-custom-properties --save-dev +``` + +Use [PostCSS Custom Properties] in your `postcss.config.js` configuration file: + +```js +const postcssCustomProperties = require('postcss-custom-properties'); + +module.exports = { + plugins: [ + postcssCustomProperties(/* pluginOptions */) + ] +} +``` + +## PostCSS Load Config + +If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). + +```bash +npm install postcss-custom-properties --save-dev +``` + +`package.json`: + +```json +{ + "postcss": { + "plugins": { + "postcss-custom-properties": {} + } + } +} +``` + +`.postcssrc.json`: + +```json +{ + "plugins": { + "postcss-custom-properties": {} + } +} +``` + +_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 postcss-custom-properties --save-dev +``` + +Use [PostCSS Custom Properties] 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: [ + [ + "postcss-custom-properties", + { + // Options + }, + ], + ], + }, + }, + }, + ], + }, + ], + }, +}; +``` + +## Create React App + +Add [React App Rewired] and [React App Rewire PostCSS] to your project: + +```bash +npm install react-app-rewired react-app-rewire-postcss postcss-custom-properties --save-dev +``` + +Use [React App Rewire PostCSS] and [PostCSS Custom Properties] in your +`config-overrides.js` file: + +```js +const reactAppRewirePostcss = require('react-app-rewire-postcss'); +const postcssCustomProperties = require('postcss-custom-properties'); + +module.exports = config => reactAppRewirePostcss(config, { + plugins: () => [ + postcssCustomProperties(/* pluginOptions */) + ] +}); +``` + +## 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 postcss-custom-properties --save-dev +``` + +Use [PostCSS Custom Properties] in your `postcss.config.json` file: + +```json +{ + "plugins": [ + "postcss-custom-properties" + ] +} +``` + +```json5 +{ + "plugins": [ + [ + "postcss-custom-properties", + { + // Optionally add plugin options + } + ] + ] +} +``` + +## Gulp + +Add [Gulp PostCSS] to your project: + +```bash +npm install gulp-postcss postcss-custom-properties --save-dev +``` + +Use [PostCSS Custom Properties] in your Gulpfile: + +```js +const postcss = require('gulp-postcss'); +const postcssCustomProperties = require('postcss-custom-properties'); + +gulp.task('css', function () { + var plugins = [ + postcssCustomProperties(/* pluginOptions */) + ]; + + return gulp.src('./src/*.css') + .pipe(postcss(plugins)) + .pipe(gulp.dest('.')); +}); +``` + +## Grunt + +Add [Grunt PostCSS] to your project: + +```bash +npm install grunt-postcss postcss-custom-properties --save-dev +``` + +Use [PostCSS Custom Properties] in your Gruntfile: + +```js +const postcssCustomProperties = require('postcss-custom-properties'); + +grunt.loadNpmTasks('grunt-postcss'); + +grunt.initConfig({ + postcss: { + options: { + processors: [ + postcssCustomProperties(/* 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 Custom Properties]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties +[React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss +[React App Rewired]: https://github.com/timarney/react-app-rewired +[Next.js]: https://nextjs.org diff --git a/plugins/postcss-custom-properties-import-export/LICENSE.md b/plugins/postcss-custom-properties-import-export/LICENSE.md new file mode 100644 index 000000000..6d7047088 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/LICENSE.md @@ -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. diff --git a/plugins/postcss-custom-properties-import-export/README.md b/plugins/postcss-custom-properties-import-export/README.md new file mode 100644 index 000000000..ab48d6d96 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/README.md @@ -0,0 +1,207 @@ +# PostCSS Custom Properties [PostCSS][postcss] + +[![NPM Version][npm-img]][npm-url] +[![CSS Standard Status][css-img]][css-url] +[![Build Status][cli-img]][cli-url] +[Discord][discord] + +[PostCSS Custom Properties] lets you use Custom Properties in CSS, following +the [CSS Custom Properties] specification. + +[!['Can I use' table](https://caniuse.bitsofco.de/image/css-variables.png)](https://caniuse.com/#feat=css-variables) + +```pcss +:root { + --color: red; +} + +h1 { + color: var(--color); +} + +/* becomes */ + +:root { + --color: red; +} + +h1 { + color: red; + color: var(--color); +} +``` + +**Note:** This plugin only processes variables that are defined in the `:root` selector. + +## Usage + +Add [PostCSS Custom Properties] to your project: + +```bash +npm install postcss-custom-properties --save-dev +``` + +Use [PostCSS Custom Properties] as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssCustomProperties = require('postcss-custom-properties'); + +postcss([ + postcssCustomProperties(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +[PostCSS Custom Properties] 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 Properties and properties using +custom properties should be preserved in their original form. By default, both +of these are preserved. + +```js +postcssCustomProperties({ + preserve: false +}); +``` + +```pcss +:root { + --color: red; +} + +h1 { + color: var(--color); +} + +/* becomes */ + +h1 { + color: red; +} +``` + +### importFrom + +The `importFrom` option specifies sources where Custom Properties can be imported +from, which might be CSS, JS, and JSON files, functions, and directly passed +objects. + +```js +postcssCustomProperties({ + importFrom: 'path/to/file.css' // => :root { --color: red } +}); +``` + +```pcss +h1 { + color: var(--color); +} + +/* becomes */ + +h1 { + color: red; +} +``` + +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 Properties using the `customProperties` or +`custom-properties` key. + +```js +postcssCustomProperties({ + importFrom: [ + 'path/to/file.css', // :root { --color: red; } + 'and/then/this.js', // module.exports = { customProperties: { '--color': 'red' } } + 'and/then/that.json', // { "custom-properties": { "--color": "red" } } + { + customProperties: { '--color': 'red' } + }, + () => { + const customProperties = { '--color': 'red' }; + + return { customProperties }; + } + ] +}); +``` + +See example imports written in [CSS](test/import-properties.css), +[JS](test/import-properties.js), and [JSON](test/import-properties.json). + +### overrideImportFromWithRoot + +The `overrideImportFromWithRoot` option determines if properties added via `importFrom` are overridden by properties that exist in `:root`. +Defaults to `false`. + +```js +postcssCustomProperties({ + overrideImportFromWithRoot: true +}); +``` + +### exportTo + +The `exportTo` option specifies destinations where Custom Properties can be exported +to, which might be CSS, JS, and JSON files, functions, and directly passed +objects. + +```js +postcssCustomProperties({ + exportTo: 'path/to/file.css' // :root { --color: red; } +}); +``` + +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 Properties using the `customProperties` or +`custom-properties` key. + +```js +const cachedObject = { customProperties: {} }; + +postcssCustomProperties({ + exportTo: [ + 'path/to/file.css', // :root { --color: red; } + 'and/then/this.js', // module.exports = { customProperties: { '--color': 'red' } } + 'and/then/this.mjs', // export const customProperties = { '--color': 'red' } } + 'and/then/that.json', // { "custom-properties": { "--color": "red" } } + 'and/then/that.scss', // $color: red; + cachedObject, + customProperties => { + customProperties // { '--color': 'red' } + } + ] +}); +``` + +See example exports written to [CSS](test/export-properties.css), +[JS](test/export-properties.js), [MJS](test/export-properties.mjs), +[JSON](test/export-properties.json) and [SCSS](test/export-properties.scss). + +### disableDeprecationNotice + +Silence the deprecation notice that is printed to the console when using `importFrom` or `exportTo`. + +> "importFrom" and "exportTo" will be removed in a future version of postcss-custom-properties. +> Check the discussion on github for more details. https://github.com/csstools/postcss-plugins/discussions/192 + +[cli-img]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml/badge.svg +[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test +[css-img]: https://cssdb.org/images/badges/custom-properties.svg +[css-url]: https://cssdb.org/#custom-properties +[discord]: https://discord.gg/bUadyRwkJS +[npm-img]: https://img.shields.io/npm/v/postcss-custom-properties.svg +[npm-url]: https://www.npmjs.com/package/postcss-custom-properties + +[CSS Custom Properties]: https://www.w3.org/TR/css-variables-1/ +[PostCSS]: https://github.com/postcss/postcss +[PostCSS Custom Properties]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties diff --git a/plugins/postcss-custom-properties-import-export/package.json b/plugins/postcss-custom-properties-import-export/package.json new file mode 100644 index 000000000..575e9465e --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/package.json @@ -0,0 +1,82 @@ +{ + "name": "@csstools/postcss-custom-properties-import-export", + "description": "Use Custom Properties Queries in CSS", + "version": "12.1.10", + "author": "Jonathan Neal ", + "contributors": [ + "Maxime Thirouin" + ], + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs", + "default": "./dist/index.mjs" + } + }, + "files": [ + "CHANGELOG.md", + "LICENSE.md", + "README.md", + "dist", + "index.d.ts" + ], + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2" + }, + "devDependencies": { + "postcss-import": "^15.0.0" + }, + "scripts": { + "build": "rollup -c ../../rollup/default.js", + "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", + "docs": "node ../../.github/bin/generate-docs/install.mjs", + "lint": "npm run lint:eslint && npm run lint:package-json", + "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", + "test": "node .tape.mjs && node .tape.cjs && npm run test:exports", + "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs", + "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs" + }, + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties#readme", + "repository": { + "type": "git", + "url": "https://github.com/csstools/postcss-plugins.git", + "directory": "plugins/postcss-custom-properties" + }, + "bugs": "https://github.com/csstools/postcss-plugins/issues", + "keywords": [ + "css", + "csswg", + "custom", + "declarations", + "postcss", + "postcss-plugin", + "properties", + "specification", + "variables", + "vars", + "w3c" + ], + "csstools": { + "exportName": "postcssCustomProperties", + "humanReadableName": "PostCSS Custom Properties" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/plugins/postcss-custom-properties-import-export/src/index.ts b/plugins/postcss-custom-properties-import-export/src/index.ts new file mode 100644 index 000000000..22b2f1187 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/index.ts @@ -0,0 +1,107 @@ +import type { PluginCreator } from 'postcss'; +import type valuesParser from 'postcss-value-parser'; + +import getCustomPropertiesFromRoot from './lib/get-custom-properties-from-root'; +import getCustomPropertiesFromImports from './lib/get-custom-properties-from-imports'; +import transformProperties from './lib/transform-properties'; +import writeCustomPropertiesToExports from './lib/write-custom-properties-to-exports'; +import type { ImportOptions, ExportOptions } from './lib/options'; + +export interface PluginOptions { + /** Do not emit warnings about "importFrom" and "exportTo" deprecations */ + disableDeprecationNotice?: boolean; + /** Determines whether Custom Properties and properties using custom properties should be preserved in their original form. */ + preserve?: boolean + + /** Specifies sources where Custom Properties can be imported from, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ + importFrom?: ImportOptions | Array + + /** Specifies destinations where Custom Properties can be exported to, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ + exportTo?: ExportOptions | Array + + /** Specifies if `importFrom` properties or `:root` properties have priority. */ + overrideImportFromWithRoot?: boolean +} + +const creator: PluginCreator = (opts?: PluginOptions) => { + // whether to preserve custom selectors and rules using them + const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : true; + const overrideImportFromWithRoot = 'overrideImportFromWithRoot' in Object(opts) ? Boolean(opts.overrideImportFromWithRoot) : false; + const disableDeprecationNotice = 'disableDeprecationNotice' in Object(opts) ? Boolean(opts.disableDeprecationNotice) : false; + + // sources to import custom selectors from + let importFrom: Array = []; + if (Array.isArray(opts?.importFrom)) { + importFrom = opts.importFrom; + } else if (opts?.importFrom) { + importFrom = [opts.importFrom]; + } + + // destinations to export custom selectors to + let exportTo: Array = []; + if (Array.isArray(opts?.exportTo)) { + exportTo = opts.exportTo; + } else if (opts?.exportTo) { + exportTo = [opts.exportTo]; + } + + // promise any custom selectors are imported + const customPropertiesPromise = getCustomPropertiesFromImports(importFrom); + + // whether to return synchronous function if no asynchronous operations are requested + const canReturnSyncFunction = importFrom.length === 0 && exportTo.length === 0; + + return { + postcssPlugin: 'postcss-custom-properties', + prepare () { + let customProperties: Map = new Map(); + + if (canReturnSyncFunction) { + return { + Once: (root) => { + customProperties = getCustomPropertiesFromRoot(root, { preserve }); + }, + Declaration: (decl) => { + transformProperties(decl, customProperties, { preserve }); + }, + OnceExit: () => { + customProperties.clear(); + }, + }; + } else { + return { + Once: async root => { + const importedCustomerProperties = (await customPropertiesPromise).entries(); + const rootCustomProperties = getCustomPropertiesFromRoot(root, { preserve: preserve }).entries(); + + if (overrideImportFromWithRoot) { + for (const [name, value] of [...importedCustomerProperties, ...rootCustomProperties]) { + customProperties.set(name, value); + } + } else { + for (const [name, value] of [...rootCustomProperties, ...importedCustomerProperties]) { + customProperties.set(name, value); + } + } + + await writeCustomPropertiesToExports(customProperties, exportTo); + }, + Declaration: (decl) => { + transformProperties(decl, customProperties, { preserve }); + }, + OnceExit: (root, { result }) => { + if (!disableDeprecationNotice && (importFrom.length > 0 || exportTo.length > 0)) { + root.warn(result, '"importFrom" and "exportTo" will be removed in a future version of postcss-custom-properties.\nWe are looking for insights and anecdotes on how these features are used so that we can design the best alternative.\nPlease let us know if our proposal will work for you.\nVisit the discussion on github for more details. https://github.com/csstools/postcss-plugins/discussions/192'); + } + + customProperties.clear(); + }, + }; + } + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts new file mode 100644 index 000000000..1296201a0 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts @@ -0,0 +1,158 @@ +import getCustomPropertiesFromRoot from './get-custom-properties-from-root'; +import path from 'path'; +import { pathToFileURL } from 'url'; +import type { ImportCustomProperties, ImportOptions } from './options'; +import valuesParser from 'postcss-value-parser'; +import { parse } from 'postcss'; +import { promises as fsp } from 'fs'; + +/* Get Custom Properties from CSS File +/* ========================================================================== */ + +async function getCustomPropertiesFromCSSFile(from) { + const css = await fsp.readFile(from); + const root = parse(css, { from : from.toString() }); + + return getCustomPropertiesFromRoot(root, { preserve: true }); +} + +/* Get Custom Properties from Object +/* ========================================================================== */ + +function getCustomPropertiesFromObject(object: ImportCustomProperties): Map { + const out: Map = new Map(); + + if ('customProperties' in object) { + for (const [name, value] of Object.entries(object.customProperties)) { + out.set(name, valuesParser(value.toString())); + } + } + + if ('custom-properties' in object) { + for (const [name, value] of Object.entries(object['custom-properties'])) { + out.set(name, valuesParser(value.toString())); + } + } + + return out; +} + +/* Get Custom Properties from JSON file +/* ========================================================================== */ + +async function getCustomPropertiesFromJSONFile(from): Promise> { + const object = await readJSON(from); + + return getCustomPropertiesFromObject(object); +} + +/* Get Custom Properties from JS file +/* ========================================================================== */ + +async function getCustomPropertiesFromJSFile(from): Promise> { + let object; + + try { + object = await import(from); + } catch (_) { + // windows support + object = await import(pathToFileURL(from).href); + } + + if ('default' in object) { + return getCustomPropertiesFromObject(object.default); + } + + return getCustomPropertiesFromObject(object); +} + +/* Get Custom Properties from Imports +/* ========================================================================== */ + +export default async function getCustomPropertiesFromImports(sources: Array): Promise> { + const sourceData = (await Promise.all(sources.map(async (source) => { + if (source instanceof Promise) { + source = await source; + } else if (source instanceof Function) { + source = await source(); + } + + if (typeof source === 'string') { + const fromPath = path.resolve(source); + const type = path.extname(fromPath).slice(1).toLowerCase(); + + return { + type: type, + from: fromPath, + }; + } + + if ('customProperties' in source && Object(source.customProperties) === source.customProperties) { + return source; + } + + if ('custom-properties' in source && Object(source['custom-properties']) === source['custom-properties']) { + return source; + } + + if ('from' in source) { + const fromPath = path.resolve(source.from); + let type = source.type; + if (!type) { + type = path.extname(fromPath).slice(1).toLowerCase(); + } + return { + type: type, + from: fromPath, + }; + } + + if (Object.keys(source).length === 0) { + // Empty `importFrom` object. + return null; + } + + return null; + }))).filter((x) => { + return !!x; + }); + + const data: Array> = await Promise.all(sourceData.map(async (partialData) => { + if (('type' in partialData) && ('from' in partialData)) { + if (partialData.type === 'css' || partialData.type === 'pcss') { + return await getCustomPropertiesFromCSSFile(partialData.from); + } + + if (partialData.type === 'js' || partialData.type === 'cjs') { + return await getCustomPropertiesFromJSFile(partialData.from); + } + + if (partialData.type === 'mjs') { + // Only works when running as a module. + return await getCustomPropertiesFromJSFile(partialData.from); + } + + if (partialData.type === 'json') { + return await getCustomPropertiesFromJSONFile(partialData.from); + } + + throw new Error('Invalid source type: ' + partialData.type); + } + + return getCustomPropertiesFromObject(partialData); + })); + + const out: Map = new Map(); + data.forEach((partialData) => { + for (const [name, value] of partialData.entries()) { + out.set(name, value); + } + }); + + return out; +} + +/* Helper utilities +/* ========================================================================== */ + +const readJSON = async from => JSON.parse((await fsp.readFile(from)).toString()); diff --git a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts new file mode 100644 index 000000000..e177478fa --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts @@ -0,0 +1,63 @@ +import valuesParser from 'postcss-value-parser'; +import { isBlockIgnored } from './is-ignored'; + +// return custom selectors from the css root, conditionally removing them +export default function getCustomPropertiesFromRoot(root, opts): Map { + // initialize custom selectors + const customPropertiesFromHtmlElement: Map = new Map(); + const customPropertiesFromRootPseudo: Map = new Map(); + const out: Map = new Map(); + + // for each html or :root rule + root.nodes.slice().forEach(rule => { + const customPropertiesObject = isHtmlRule(rule) + ? customPropertiesFromHtmlElement + : isRootRule(rule) + ? customPropertiesFromRootPseudo + : null; + + // for each custom property + if (customPropertiesObject) { + rule.nodes.slice().forEach(decl => { + if (decl.variable && !isBlockIgnored(decl)) { + const { prop } = decl; + + // write the parsed value to the custom property + customPropertiesObject.set(prop, valuesParser(decl.value)); + + // conditionally remove the custom property declaration + if (!opts.preserve) { + decl.remove(); + } + } + }); + + // conditionally remove the empty html or :root rule + if (!opts.preserve && isEmptyParent(rule) && !isBlockIgnored(rule)) { + rule.remove(); + } + } + }); + + for (const [name, value] of customPropertiesFromHtmlElement.entries()) { + out.set(name, value); + } + + for (const [name, value] of customPropertiesFromRootPseudo.entries()) { + out.set(name, value); + } + + // return all custom properties, preferring :root properties over html properties + return out; +} + +// match html and :root rules +const htmlSelectorRegExp = /^html$/i; +const rootSelectorRegExp = /^:root$/i; + +// whether the node is an html or :root rule +const isHtmlRule = node => node.type === 'rule' && node.selector.split(',').some(item => htmlSelectorRegExp.test(item)) && Object(node.nodes).length; +const isRootRule = node => node.type === 'rule' && node.selector.split(',').some(item => rootSelectorRegExp.test(item)) && Object(node.nodes).length; + +// whether the node is a parent without children +const isEmptyParent = node => Object(node.nodes).length === 0; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts b/plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts new file mode 100644 index 000000000..e1c5ccc5c --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts @@ -0,0 +1,20 @@ +function isBlockIgnored(ruleOrDeclaration) { + const rule = ruleOrDeclaration.selector ? + ruleOrDeclaration : ruleOrDeclaration.parent; + + return /(!\s*)?postcss-custom-properties:\s*off\b/i.test(rule.toString()); +} + +function isRuleIgnored(rule) { + const previous = rule.prev(); + + return Boolean(isBlockIgnored(rule) || + previous && + previous.type === 'comment' && + /(!\s*)?postcss-custom-properties:\s*ignore\s+next\b/i.test(previous.text)); +} + +export { + isBlockIgnored, + isRuleIgnored, +}; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/options.ts b/plugins/postcss-custom-properties-import-export/src/lib/options.ts new file mode 100644 index 000000000..6db403f7f --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/options.ts @@ -0,0 +1,14 @@ + +export type ImportFromSource = { from: string, type?: string } | string; +export type ImportCustomProperties = { customProperties?: Record, 'custom-properties'?: Record }; +export type ImportAsFunction = () => ImportFromSource | ImportCustomProperties +export type ImportAsPromise = Promise +export type ImportAsFunctionPromise = () => Promise +export type ImportOptions = ImportFromSource | ImportCustomProperties | ImportAsFunction | ImportAsPromise | ImportAsFunctionPromise; + +export type ExportJSONFunction = (customProperties?: Record) => Record; +export type ExportToSource = { to: string, type?: string, toJSON: ExportJSONFunction } | string; +export type ExportCustomProperties = { customProperties?: Record, 'custom-properties'?: Record, toJSON: ExportJSONFunction }; +export type ExportAsFunction = (ExportCustomProperties) => void +export type ExportAsFunctionPromise = (ExportCustomProperties) => Promise +export type ExportOptions = ExportToSource | ExportCustomProperties | ExportAsFunction | ExportAsFunctionPromise; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts b/plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts new file mode 100644 index 000000000..da2a8147a --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts @@ -0,0 +1,86 @@ +import valuesParser from 'postcss-value-parser'; +import transformValueAST from './transform-value-ast'; +import { isRuleIgnored } from './is-ignored'; +import { Declaration } from 'postcss'; + +// transform custom pseudo selectors with custom selectors +export default (decl, customProperties, opts) => { + if (isTransformableDecl(decl) && !isRuleIgnored(decl)) { + const originalValue = decl.value; + const valueAST = valuesParser(originalValue); + let value = transformValueAST(valueAST, customProperties); + + // protect against circular references + const valueSet = new Set(); + + while (value.includes('--') && value.includes('var(') && !valueSet.has(value)) { + valueSet.add(value); + const parsedValueAST = valuesParser(value); + value = transformValueAST(parsedValueAST, customProperties); + } + + // conditionally transform values that have changed + if (value !== originalValue) { + if (parentHasExactFallback(decl, value)) { + if (!opts.preserve) { + decl.remove(); + } + + return; + } + + if (opts.preserve) { + const beforeDecl = decl.cloneBefore({ value }); + + if (hasTrailingComment(beforeDecl)) { + beforeDecl.raws.value.value = beforeDecl.value.replace(trailingCommentRegExp, '$1'); + beforeDecl.raws.value.raw = beforeDecl.raws.value.value + beforeDecl.raws.value.raw.replace(trailingCommentRegExp, '$2'); + } + } else { + decl.value = value; + + if (hasTrailingComment(decl)) { + decl.raws.value.value = decl.value.replace(trailingCommentRegExp, '$1'); + decl.raws.value.raw = decl.raws.value.value + decl.raws.value.raw.replace(trailingCommentRegExp, '$2'); + } + } + } + } +}; + +// match custom properties + +// whether the declaration should be potentially transformed +const isTransformableDecl = decl => !decl.variable && decl.value.includes('--') && decl.value.includes('var('); + +// whether the declaration has a trailing comment +const hasTrailingComment = decl => 'value' in Object(Object(decl.raws).value) && 'raw' in decl.raws.value && trailingCommentRegExp.test(decl.raws.value.raw); +const trailingCommentRegExp = /^([\W\w]+)(\s*\/\*[\W\w]+?\*\/)$/; + +function parentHasExactFallback(decl: Declaration, value: string): boolean { + if (!decl || !decl.parent) { + return false; + } + + let hasFallback = false; + const declIndex = decl.parent.index(decl); + decl.parent.each((sibling, index) => { + if (sibling === decl) { + return false; + } + + if (index >= declIndex) { + return false; + } + + if (sibling.type !== 'decl') { + return; + } + + if (sibling.prop.toLowerCase() === decl.prop.toLowerCase() && sibling.value === value) { + hasFallback = true; + } + }); + + return hasFallback; +} diff --git a/plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts b/plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts new file mode 100644 index 000000000..10d42b10b --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts @@ -0,0 +1,50 @@ +export default function transformValueAST(root, customProperties) { + if (root.nodes && root.nodes.length) { + root.nodes.slice().forEach((child) => { + if (isVarFunction(child)) { + const [propertyNode, ...fallbacks] = child.nodes.filter((node) => node.type !== 'div'); + const { value: name } = propertyNode; + const index = root.nodes.indexOf(child); + + if (customProperties.has(name)) { + // Direct match of a custom property to a parsed value + const nodes = customProperties.get(name).nodes; + + // Re-transform nested properties without given one to avoid circular from keeping this forever + reTransformValueAST({ nodes }, customProperties, name); + + if (index > -1) { + root.nodes.splice(index, 1, ...nodes); + } + } else if (fallbacks.length) { + // No match, but fallback available + if (index > -1) { + root.nodes.splice(index, 1, ...child.nodes.slice(child.nodes.indexOf(fallbacks[0]))); + } + + transformValueAST(root, customProperties); + } + } else { + // Transform child nodes of current child + transformValueAST(child, customProperties); + } + }); + } + + return root.toString(); +} + +// reTransform the current ast without a custom property (to prevent recursion) +function reTransformValueAST(root, customProperties, withoutProperty) { + const nextCustomProperties = new Map(customProperties); + + nextCustomProperties.delete(withoutProperty); + + return transformValueAST(root, nextCustomProperties); +} + +// match var() functions +const varRegExp = /^var$/i; + +// whether the node is a var() function +const isVarFunction = node => node.type === 'function' && varRegExp.test(node.value) && Object(node.nodes).length > 0; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts b/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts new file mode 100644 index 000000000..bbd0b1cb6 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts @@ -0,0 +1,159 @@ +import type valuesParser from 'postcss-value-parser'; + +import { promises as fsp } from 'fs'; +import path from 'path'; +import type { ExportOptions } from './options'; + +/* Write Custom Properties to CSS File +/* ========================================================================== */ + +async function writeCustomPropertiesToCssFile(to, customProperties) { + const cssContent = Object.keys(customProperties).reduce((cssLines, name) => { + cssLines.push(`\t${name}: ${customProperties[name]};`); + + return cssLines; + }, []).join('\n'); + const css = `:root {\n${cssContent}\n}\n`; + + await fsp.writeFile(to, css); +} + +/* Write Custom Properties to SCSS File +/* ========================================================================== */ + +async function writeCustomPropertiesToScssFile(to, customProperties) { + const scssContent = Object.keys(customProperties).reduce((scssLines, name) => { + const scssName = name.replace('--', '$'); + scssLines.push(`${scssName}: ${customProperties[name]};`); + + return scssLines; + }, []).join('\n'); + const scss = `${scssContent}\n`; + + await fsp.writeFile(to, scss); +} + +/* Write Custom Properties to JSON file +/* ========================================================================== */ + +async function writeCustomPropertiesToJsonFile(to, customProperties) { + const jsonContent = JSON.stringify({ + 'custom-properties': customProperties, + }, null, ' '); + const json = `${jsonContent}\n`; + + await fsp.writeFile(to, json); +} + +/* Write Custom Properties to Common JS file +/* ========================================================================== */ + +async function writeCustomPropertiesToCjsFile(to, customProperties) { + const jsContents = Object.keys(customProperties).reduce((jsLines, name) => { + jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(customProperties[name])}'`); + + return jsLines; + }, []).join(',\n'); + const js = `module.exports = {\n\tcustomProperties: {\n${jsContents}\n\t}\n};\n`; + + await fsp.writeFile(to, js); +} + +/* Write Custom Properties to Module JS file +/* ========================================================================== */ + +async function writeCustomPropertiesToMjsFile(to, customProperties) { + const mjsContents = Object.keys(customProperties).reduce((mjsLines, name) => { + mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(customProperties[name])}'`); + + return mjsLines; + }, []).join(',\n'); + const mjs = `export const customProperties = {\n${mjsContents}\n};\n`; + + await fsp.writeFile(to, mjs); +} + +/* Write Custom Properties to Exports +/* ========================================================================== */ + +export default function writeCustomPropertiesToExports(customProperties, destinations: Array) { + return Promise.all(destinations.map(async destination => { + if (destination instanceof Function) { + await destination(defaultCustomPropertiesToJSONObject(customProperties)); + return; + } + + if (typeof destination === 'string') { + const toPath = path.resolve(destination); + const type = path.extname(toPath).slice(1).toLowerCase(); + + await writePropertiesToFile(toPath, type, defaultCustomPropertiesToJSONObject(customProperties)); + return; + } + + // transformer for Custom Properties into a JSON-compatible object + let customPropertiesAsJSONObject = {}; + if ('toJSON' in destination) { + customPropertiesAsJSONObject = destination.toJSON(defaultCustomPropertiesToJSONObject(customProperties)); + } else { + customPropertiesAsJSONObject = defaultCustomPropertiesToJSONObject(customProperties); + } + + if ('to' in destination) { + const toPath = path.resolve(destination.to); + let type = destination.type; + if (!type) { + type = path.extname(toPath).slice(1).toLowerCase(); + } + + await writePropertiesToFile(toPath, type, customPropertiesAsJSONObject); + return; + } + + if ('customProperties' in destination) { + // write directly to an object as customProperties + destination.customProperties = customPropertiesAsJSONObject; + } else if ('custom-properties' in destination) { + // write directly to an object as custom-properties + destination['custom-properties'] = customPropertiesAsJSONObject; + } + })); +} + +async function writePropertiesToFile(to: string, type: string, customProperties: Record) { + if (type === 'css') { + await writeCustomPropertiesToCssFile(to, customProperties); + } + + if (type === 'scss') { + await writeCustomPropertiesToScssFile(to, customProperties); + } + + if (type === 'js') { + await writeCustomPropertiesToCjsFile(to, customProperties); + } + + if (type === 'json') { + await writeCustomPropertiesToJsonFile(to, customProperties); + } + + if (type === 'mjs') { + await writeCustomPropertiesToMjsFile(to, customProperties); + } +} + +/* Helper utilities +/* ========================================================================== */ + +function defaultCustomPropertiesToJSONObject(customProperties: Map): Record { + const out = {}; + for (const [name, value] of customProperties.entries()) { + out[name] = value.toString(); + } + + return out; +} + +const escapeForJS = (string) => { + return string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r'); +}; diff --git a/plugins/postcss-custom-properties-import-export/test/_import.mjs b/plugins/postcss-custom-properties-import-export/test/_import.mjs new file mode 100644 index 000000000..98f876766 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/_import.mjs @@ -0,0 +1,17 @@ +import assert from 'assert'; +import plugin from 'postcss-custom-properties'; +import postcss from 'postcss'; +import fs from 'fs'; + +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); + +postcss([plugin({ importFrom: 'test/import-properties.cjs' })]). + process(fs.readFileSync('test/basic.css', 'utf8'), {from: 'test/basic.css'}). + then(); + +postcss([plugin({ importFrom: 'test/import-properties.mjs' })]). + process(fs.readFileSync('test/basic.css', 'utf8'), {from: 'test/basic.css'}). + then(); diff --git a/plugins/postcss-custom-properties-import-export/test/_require.cjs b/plugins/postcss-custom-properties-import-export/test/_require.cjs new file mode 100644 index 000000000..a9a3a0897 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/_require.cjs @@ -0,0 +1,13 @@ +const assert = require('assert'); +const plugin = require('postcss-custom-properties'); +const postcss = require('postcss'); +const fs = require('fs'); + +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); + +postcss([plugin({ importFrom: 'test/import-properties.cjs' })]). + process(fs.readFileSync('test/basic.css', 'utf8'), {from: 'test/basic.css'}). + then(); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.css b/plugins/postcss-custom-properties-import-export/test/basic.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.expect.css new file mode 100644 index 000000000..849754a3d --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.expect.css @@ -0,0 +1,183 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: rgb(255, 0, 0); + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: blue; + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; + margin: var(--margin); +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; + font-family: var(--font-family); +} + +.test--fallback { + color: blue; + color: var(--color-2, blue); +} + +.test--color_w_var { + color: rgb(255, 0, 0); + color: var(--ref-color); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: #053; + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css new file mode 100644 index 000000000..849754a3d --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css @@ -0,0 +1,183 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: rgb(255, 0, 0); + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: blue; + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; + margin: var(--margin); +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; + font-family: var(--font-family); +} + +.test--fallback { + color: blue; + color: var(--color-2, blue); +} + +.test--color_w_var { + color: rgb(255, 0, 0); + color: var(--ref-color); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: #053; + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css new file mode 100644 index 000000000..384a4d6f1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css @@ -0,0 +1,129 @@ +:root { + color: rgb(0, 0, 0); +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: yellow; +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(0, 0, 0); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(0, 0, 0); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(0,0,0) a(.15)); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; +} + +.test--fallback { + color: yellow; +} + +.test--color_w_var { + color: rgb(0, 0, 0); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: 10; +} + +.test--nested-fallback { + z-index: 1; +} + +.text--calc { + width: calc((100% - 1px) + 10px); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 100%); +} + +.test--loose-formatting { + color: rgb(0, 0, 0)/*rtl:red*/; +} + +.test--combined-selector { + color: #053; +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css new file mode 100644 index 000000000..a784a4b6c --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css @@ -0,0 +1,129 @@ +:root { + color: rgb(255, 0, 0); +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: yellow; +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; +} + +.test--fallback { + color: yellow; +} + +.test--color_w_var { + color: rgb(255, 0, 0); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: 10; +} + +.test--nested-fallback { + z-index: 1; +} + +.text--calc { + width: calc((100% - 1px) + 10px); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; +} + +.test--combined-selector { + color: #053; +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css new file mode 100644 index 000000000..be1b4f513 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css @@ -0,0 +1,184 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: rgb(255, 0, 0); + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: yellow; + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; + margin: var(--margin); +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; + font-family: var(--font-family); +} + +.test--fallback { + color: yellow; + color: var(--color-2, blue); +} + +.test--color_w_var { + color: rgb(255, 0, 0); + color: var(--ref-color); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: 10; + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: #053; + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css new file mode 100644 index 000000000..fcd1ef3ac --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css @@ -0,0 +1,129 @@ +:root { + color: rgb(255, 0, 0); +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: blue; +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; +} + +.test--fallback { + color: blue; +} + +.test--color_w_var { + color: rgb(255, 0, 0); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: 1; +} + +.text--calc { + width: calc((100% - 1px) + 10px); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; +} + +.test--combined-selector { + color: #053; +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/export-properties.css b/plugins/postcss-custom-properties-import-export/test/export-properties.css new file mode 100644 index 000000000..6c5bc15ff --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/export-properties.css @@ -0,0 +1,20 @@ +:root { + --ref-color: var(--color); + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + --theme-color: #053; +} diff --git a/plugins/postcss-custom-properties-import-export/test/export-properties.js b/plugins/postcss-custom-properties-import-export/test/export-properties.js new file mode 100644 index 000000000..4a881c6cd --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/export-properties.js @@ -0,0 +1,22 @@ +module.exports = { + customProperties: { + '--ref-color': 'var(--color)', + '--color': 'rgb(255, 0, 0)', + '--color-h': '0', + '--color-s': '100%', + '--color-l': '50%', + '--color-hsl': 'hsl(var(--color-h), var(--color-s), var(--color-l))', + '--circular': 'var(--circular-2)', + '--circular-2': 'var(--circular)', + '--margin': '0 10px 20px 30px', + '--shadow-color': 'rgb(255,0,0)', + '--shadow': '0 6px 14px 0 color(var(--shadow-color) a(.15))', + '--font-family': '"Open Sans", sans-serif', + '--url-1': 'url("/my/path")', + '--url-2': 'url(\'/my/path\')', + '--url-3': 'url(/my/path)', + '--url-4': 'url(data:image/png;bm90LWFuZC1pbWFnZQ==)', + '--✅-size': '2em', + '--theme-color': '#053' + } +}; diff --git a/plugins/postcss-custom-properties-import-export/test/export-properties.json b/plugins/postcss-custom-properties-import-export/test/export-properties.json new file mode 100644 index 000000000..723f042ba --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/export-properties.json @@ -0,0 +1,22 @@ +{ + "custom-properties": { + "--ref-color": "var(--color)", + "--color": "rgb(255, 0, 0)", + "--color-h": "0", + "--color-s": "100%", + "--color-l": "50%", + "--color-hsl": "hsl(var(--color-h), var(--color-s), var(--color-l))", + "--circular": "var(--circular-2)", + "--circular-2": "var(--circular)", + "--margin": "0 10px 20px 30px", + "--shadow-color": "rgb(255,0,0)", + "--shadow": "0 6px 14px 0 color(var(--shadow-color) a(.15))", + "--font-family": "\"Open Sans\", sans-serif", + "--url-1": "url(\"/my/path\")", + "--url-2": "url('/my/path')", + "--url-3": "url(/my/path)", + "--url-4": "url(data:image/png;bm90LWFuZC1pbWFnZQ==)", + "--✅-size": "2em", + "--theme-color": "#053" + } +} diff --git a/plugins/postcss-custom-properties-import-export/test/export-properties.mjs b/plugins/postcss-custom-properties-import-export/test/export-properties.mjs new file mode 100644 index 000000000..30e030b94 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/export-properties.mjs @@ -0,0 +1,20 @@ +export const customProperties = { + '--ref-color': 'var(--color)', + '--color': 'rgb(255, 0, 0)', + '--color-h': '0', + '--color-s': '100%', + '--color-l': '50%', + '--color-hsl': 'hsl(var(--color-h), var(--color-s), var(--color-l))', + '--circular': 'var(--circular-2)', + '--circular-2': 'var(--circular)', + '--margin': '0 10px 20px 30px', + '--shadow-color': 'rgb(255,0,0)', + '--shadow': '0 6px 14px 0 color(var(--shadow-color) a(.15))', + '--font-family': '"Open Sans", sans-serif', + '--url-1': 'url("/my/path")', + '--url-2': 'url(\'/my/path\')', + '--url-3': 'url(/my/path)', + '--url-4': 'url(data:image/png;bm90LWFuZC1pbWFnZQ==)', + '--✅-size': '2em', + '--theme-color': '#053' +}; diff --git a/plugins/postcss-custom-properties-import-export/test/export-properties.scss b/plugins/postcss-custom-properties-import-export/test/export-properties.scss new file mode 100644 index 000000000..a920224bb --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/export-properties.scss @@ -0,0 +1,18 @@ +$ref-color: var(--color); +$color: rgb(255, 0, 0); +$color-h: 0; +$color-s: 100%; +$color-l: 50%; +$color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); +$circular: var(--circular-2); +$circular-2: var(--circular); +$margin: 0 10px 20px 30px; +$shadow-color: rgb(255,0,0); +$shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); +$font-family: "Open Sans", sans-serif; +$url-1: url("/my/path"); +$url-2: url('/my/path'); +$url-3: url(/my/path); +$url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); +$✅-size: 2em; +$theme-color: #053; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties-2.cjs b/plugins/postcss-custom-properties-import-export/test/import-properties-2.cjs new file mode 100644 index 000000000..a9ac86761 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties-2.cjs @@ -0,0 +1,6 @@ +module.exports = { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + }, +}; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties-2.css b/plugins/postcss-custom-properties-import-export/test/import-properties-2.css new file mode 100644 index 000000000..3f814966a --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties-2.css @@ -0,0 +1,4 @@ +:root { + --color: rgb(255, 0, 0); + --color-2: yellow; +} diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties-2.js b/plugins/postcss-custom-properties-import-export/test/import-properties-2.js new file mode 100644 index 000000000..a9ac86761 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties-2.js @@ -0,0 +1,6 @@ +module.exports = { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + }, +}; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties-2.mjs b/plugins/postcss-custom-properties-import-export/test/import-properties-2.mjs new file mode 100644 index 000000000..22617d2ef --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties-2.mjs @@ -0,0 +1,6 @@ +export default { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + }, +}; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties.cjs b/plugins/postcss-custom-properties-import-export/test/import-properties.cjs new file mode 100644 index 000000000..4b2551bb6 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties.cjs @@ -0,0 +1,6 @@ +module.exports = { + customProperties: { + '--ref-color': 'var(--color)', + '--z-index': 10, + }, +}; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties.css b/plugins/postcss-custom-properties-import-export/test/import-properties.css new file mode 100644 index 000000000..a01ea3e89 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties.css @@ -0,0 +1,4 @@ +:root { + --ref-color: var(--color); + --z-index: 10; +} diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties.js b/plugins/postcss-custom-properties-import-export/test/import-properties.js new file mode 100644 index 000000000..4b2551bb6 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties.js @@ -0,0 +1,6 @@ +module.exports = { + customProperties: { + '--ref-color': 'var(--color)', + '--z-index': 10, + }, +}; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties.json b/plugins/postcss-custom-properties-import-export/test/import-properties.json new file mode 100644 index 000000000..d4125fc5d --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties.json @@ -0,0 +1,8 @@ +{ + "custom-properties": { + "--color": "rgb(255, 0, 0)", + "--color-2": "yellow", + "--ref-color": "var(--color)", + "--z-index": 10 + } +} diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties.mjs b/plugins/postcss-custom-properties-import-export/test/import-properties.mjs new file mode 100644 index 000000000..78c3b75a2 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties.mjs @@ -0,0 +1,6 @@ +export default { + customProperties: { + '--ref-color': 'var(--color)', + '--z-index': 10, + }, +}; diff --git a/plugins/postcss-custom-properties-import-export/test/import-properties.pcss b/plugins/postcss-custom-properties-import-export/test/import-properties.pcss new file mode 100644 index 000000000..a01ea3e89 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import-properties.pcss @@ -0,0 +1,4 @@ +:root { + --ref-color: var(--color); + --z-index: 10; +} diff --git a/plugins/postcss-custom-properties-import-export/test/import.css b/plugins/postcss-custom-properties-import-export/test/import.css new file mode 100644 index 000000000..64636398d --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import.css @@ -0,0 +1,5 @@ +@import "./imported-file.css"; + +a { + color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/import.expect.css b/plugins/postcss-custom-properties-import-export/test/import.expect.css new file mode 100644 index 000000000..c7fcc0ac0 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/import.expect.css @@ -0,0 +1,8 @@ +:root { + --color: blue; +} + +a { + color: blue; + color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/imported-file.css b/plugins/postcss-custom-properties-import-export/test/imported-file.css new file mode 100644 index 000000000..eb0ff0d59 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/imported-file.css @@ -0,0 +1,3 @@ +:root { + --color: blue; +} diff --git a/plugins/postcss-custom-properties-import-export/tsconfig.json b/plugins/postcss-custom-properties-import-export/tsconfig.json new file mode 100644 index 000000000..2e428a8c2 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": ".", + "module": "es2020" + }, + "include": ["./src/**/*"], + "exclude": ["dist"], +} diff --git a/plugins/postcss-custom-selectors-import-export/.gitignore b/plugins/postcss-custom-selectors-import-export/.gitignore new file mode 100644 index 000000000..7172b04f1 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/.gitignore @@ -0,0 +1,6 @@ +node_modules +package-lock.json +yarn.lock +*.result.css +*.result.css.map +dist/* diff --git a/plugins/postcss-custom-selectors-import-export/.nvmrc b/plugins/postcss-custom-selectors-import-export/.nvmrc new file mode 100644 index 000000000..f0b10f153 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/.nvmrc @@ -0,0 +1 @@ +v16.13.1 diff --git a/plugins/postcss-custom-selectors-import-export/.tape.cjs b/plugins/postcss-custom-selectors-import-export/.tape.cjs new file mode 100644 index 000000000..c2ec61cdf --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/.tape.cjs @@ -0,0 +1,210 @@ +const postcssTape = require('../../packages/postcss-tape/dist/index.cjs'); +const plugin = require('@csstools/postcss-custom-selectors-import-export'); + +postcssTape(plugin)({ + 'basic': { + message: 'does nothing when no options are set', + }, + 'basic-import': { + message: 'supports { importFrom: { customSelectors: { ... } } } usage', + options: { + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + } + } + }, + 'basic-import:fn': { + message: 'supports { importFrom() } usage', + options: { + importFrom() { + return { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }; + } + }, + }, + 'basic-import:fn-promise': { + message: 'supports { async importFrom() } usage', + options: { + importFrom() { + return new Promise(resolve => { + resolve({ + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }) + }); + } + }, + }, + 'basic-import:json': { + message: 'supports { importFrom: "test/import-selectors.json" } usage', + options: { + importFrom: 'test/import-selectors.json' + }, + }, + 'basic-import:js': { + message: 'supports { importFrom: "test/import-selectors.js" } usage', + options: { + importFrom: 'test/import-selectors.js' + }, + }, + 'basic-import:css': { + message: 'supports { importFrom: "test/import-selectors.css" } usage', + options: { + importFrom: 'test/import-selectors.css' + }, + }, + 'basic-import:css-from': { + message: 'supports { importFrom: { from: "test/import-selectors.css" } } usage', + options: { + importFrom: { from: 'test/import-selectors.css' } + }, + }, + 'basic-import:css-from-multiple-files': { + message: 'supports { importFrom: ["test/empty.css", "test/import-selectors.css"] } usage', + options: { + importFrom: ["test/empty.css", "test/import-selectors.css"] + }, + }, + 'basic-import:css-from-type': { + message: 'supports { importFrom: [ { from: "test/import-selectors.css", type: "css" } ] } usage', + options: { + importFrom: [{ from: 'test/import-selectors.css', type: 'css' }] + }, + }, + 'basic-import:empty': { + message: 'supports { importFrom: {} } usage', + options: { + importFrom: {} + } + }, + 'basic:export': { + message: 'supports { exportTo: { customSelectors: { ... } } } usage', + options: { + exportTo: (global.__exportSelectorObject = global.__exportSelectorObject || { + customSelectors: null + }) + }, + after() { + if (__exportSelectorObject.customSelectors[':--foo'] !== '.foo') { + throw new Error('The exportTo function failed'); + } + } + }, + 'basic:export-fn': { + message: 'supports { exportTo() } usage', + options: { + exportTo(customProperties) { + if (customProperties[':--foo'] !== '.foo') { + throw new Error('The exportTo function failed'); + } + } + }, + }, + 'basic:export-fn-promise': { + message: 'supports { async exportTo() } usage', + options: { + exportTo(customProperties) { + return new Promise((resolve, reject) => { + if (customProperties[':--foo'] !== '.foo') { + reject('The exportTo function failed'); + } else { + resolve(); + } + }); + } + }, + }, + 'basic:export-json': { + message: 'supports { exportTo: "test/export-selectors.json" } usage', + options: { + exportTo: 'test/export-selectors.json' + }, + before() { + global.__exportSelectorsString = require('fs').readFileSync('test/export-selectors.json', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== require('fs').readFileSync('test/export-selectors.json', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-js': { + message: 'supports { exportTo: "test/export-selectors.js" } usage', + options: { + exportTo: 'test/export-selectors.js' + }, + before() { + global.__exportSelectorsString = require('fs').readFileSync('test/export-selectors.js', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== require('fs').readFileSync('test/export-selectors.js', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-mjs': { + message: 'supports { exportTo: "test/export-selectors.mjs" } usage', + options: { + exportTo: 'test/export-selectors.mjs' + }, + before() { + global.__exportSelectorsString = require('fs').readFileSync('test/export-selectors.mjs', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== require('fs').readFileSync('test/export-selectors.mjs', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-css': { + message: 'supports { exportTo: "test/export-selectors.css" } usage', + options: { + exportTo: 'test/export-selectors.css' + }, + before() { + global.__exportSelectorsString = require('fs').readFileSync('test/export-selectors.css', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== require('fs').readFileSync('test/export-selectors.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-css-to': { + message: 'supports { exportTo: { to: "test/export-selectors.css" } } usage', + options: { + exportTo: { to: 'test/export-selectors.css' } + }, + before() { + global.__exportSelectorsString = require('fs').readFileSync('test/export-selectors.css', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== require('fs').readFileSync('test/export-selectors.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-css-to-type': { + message: 'supports { exportTo: { to: "test/export-selectors.css", type: "css" } } usage', + options: { + exportTo: { to: 'test/export-selectors.css', type: 'css' } + }, + before() { + global.__exportSelectorsString = require('fs').readFileSync('test/export-selectors.css', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== require('fs').readFileSync('test/export-selectors.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + } +}); diff --git a/plugins/postcss-custom-selectors-import-export/.tape.mjs b/plugins/postcss-custom-selectors-import-export/.tape.mjs new file mode 100644 index 000000000..fb88f8fad --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/.tape.mjs @@ -0,0 +1,232 @@ +import postcssTape from '../../packages/postcss-tape/dist/index.mjs'; +import plugin from '@csstools/postcss-custom-selectors-import-export'; +import polyfillPlugin from 'postcss-custom-selectors'; +import fs from 'fs'; + +postcssTape(plugin)({ + 'basic': { + message: 'does nothing when no options are set', + }, + 'basic-import': { + message: 'supports { importFrom: { customSelectors: { ... } } } usage', + options: { + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + } + } + }, + 'basic-import:with-polyfill-plugin': { + message: 'works correctly together with the polyfill', + plugins: [ + plugin({ + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + } + }), + polyfillPlugin(), + ] + }, + 'basic-import:fn': { + message: 'supports { importFrom() } usage', + options: { + importFrom() { + return { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }; + } + }, + }, + 'basic-import:fn-promise': { + message: 'supports { async importFrom() } usage', + options: { + importFrom() { + return new Promise(resolve => { + resolve({ + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }) + }); + } + }, + }, + 'basic-import:json': { + message: 'supports { importFrom: "test/import-selectors.json" } usage', + options: { + importFrom: 'test/import-selectors.json' + }, + }, + 'basic-import:js': { + message: 'supports { importFrom: "test/import-selectors.js" } usage', + options: { + importFrom: 'test/import-selectors.js' + }, + }, + 'basic-import:mjs': { + message: 'supports { importFrom: "test/import-selectors.mjs" } usage', + options: { + importFrom: 'test/import-selectors.mjs' + }, + }, + 'basic-import:css': { + message: 'supports { importFrom: "test/import-selectors.css" } usage', + options: { + importFrom: 'test/import-selectors.css' + }, + }, + 'basic-import:css-from': { + message: 'supports { importFrom: { from: "test/import-selectors.css" } } usage', + options: { + importFrom: { from: 'test/import-selectors.css' } + }, + }, + 'basic-import:css-from-multiple-files': { + message: 'supports { importFrom: ["test/empty.css", "test/import-selectors.css"] } usage', + options: { + importFrom: ["test/empty.css", "test/import-selectors.css"] + }, + }, + 'basic-import:css-from-type': { + message: 'supports { importFrom: [ { from: "test/import-selectors.css", type: "css" } ] } usage', + options: { + importFrom: [{ from: 'test/import-selectors.css', type: 'css' }] + }, + }, + 'basic-import:empty': { + message: 'supports { importFrom: {} } usage', + options: { + importFrom: {} + } + }, + 'basic:export': { + message: 'supports { exportTo: { customSelectors: { ... } } } usage', + options: { + exportTo: (global.__exportSelectorObject = global.__exportSelectorObject || { + customSelectors: null + }) + }, + after() { + if (__exportSelectorObject.customSelectors[':--foo'] !== '.foo') { + throw new Error('The exportTo function failed'); + } + } + }, + 'basic:export-fn': { + message: 'supports { exportTo() } usage', + options: { + exportTo(customProperties) { + if (customProperties[':--foo'] !== '.foo') { + throw new Error('The exportTo function failed'); + } + } + }, + }, + 'basic:export-fn-promise': { + message: 'supports { async exportTo() } usage', + options: { + exportTo(customProperties) { + return new Promise((resolve, reject) => { + if (customProperties[':--foo'] !== '.foo') { + reject('The exportTo function failed'); + } else { + resolve(); + } + }); + } + }, + }, + 'basic:export-json': { + message: 'supports { exportTo: "test/export-selectors.json" } usage', + options: { + exportTo: 'test/export-selectors.json' + }, + before() { + global.__exportSelectorsString = fs.readFileSync('test/export-selectors.json', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== fs.readFileSync('test/export-selectors.json', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-js': { + message: 'supports { exportTo: "test/export-selectors.js" } usage', + options: { + exportTo: 'test/export-selectors.js' + }, + before() { + global.__exportSelectorsString = fs.readFileSync('test/export-selectors.js', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== fs.readFileSync('test/export-selectors.js', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-mjs': { + message: 'supports { exportTo: "test/export-selectors.mjs" } usage', + options: { + exportTo: 'test/export-selectors.mjs' + }, + before() { + global.__exportSelectorsString = fs.readFileSync('test/export-selectors.mjs', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== fs.readFileSync('test/export-selectors.mjs', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-css': { + message: 'supports { exportTo: "test/export-selectors.css" } usage', + options: { + exportTo: 'test/export-selectors.css' + }, + before() { + global.__exportSelectorsString = fs.readFileSync('test/export-selectors.css', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== fs.readFileSync('test/export-selectors.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-css-to': { + message: 'supports { exportTo: { to: "test/export-selectors.css" } } usage', + options: { + exportTo: { to: 'test/export-selectors.css' } + }, + before() { + global.__exportSelectorsString = fs.readFileSync('test/export-selectors.css', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== fs.readFileSync('test/export-selectors.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + }, + 'basic:export-css-to-type': { + message: 'supports { exportTo: { to: "test/export-selectors.css", type: "css" } } usage', + options: { + exportTo: { to: 'test/export-selectors.css', type: 'css' } + }, + before() { + global.__exportSelectorsString = fs.readFileSync('test/export-selectors.css', 'utf8'); + }, + after() { + if (global.__exportSelectorsString !== fs.readFileSync('test/export-selectors.css', 'utf8')) { + throw new Error('The original file did not match the freshly exported copy'); + } + }, + } +}); diff --git a/plugins/postcss-custom-selectors-import-export/CHANGELOG.md b/plugins/postcss-custom-selectors-import-export/CHANGELOG.md new file mode 100644 index 000000000..50e5e4e99 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changes to PostCSS Custom Selectors Import/Export + +### 1.0.0 (Unreleased) + +- Initial version diff --git a/plugins/postcss-custom-selectors-import-export/INSTALL.md b/plugins/postcss-custom-selectors-import-export/INSTALL.md new file mode 100644 index 000000000..2f0d0317e --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/INSTALL.md @@ -0,0 +1,257 @@ +# Installing PostCSS Custom Selectors Import/Export + +[PostCSS Custom Selectors Import/Export] runs in all Node environments, with special instructions for: + +- [Node](#node) +- [PostCSS CLI](#postcss-cli) +- [PostCSS Load Config](#postcss-load-config) +- [Webpack](#webpack) +- [Create React App](#create-react-app) +- [Next.js](#nextjs) +- [Gulp](#gulp) +- [Grunt](#grunt) + +## Node + +Add [PostCSS Custom Selectors Import/Export] to your project: + +```bash +npm install postcss @csstools/postcss-custom-selectors-import-export --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); + +postcss([ + postcssCustomSelectorsImportExport(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +```js +// esm +import postcss from 'postcss'; +import postcssCustomSelectorsImportExport from '@csstools/postcss-custom-selectors-import-export'; + +postcss([ + postcssCustomSelectorsImportExport(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +## PostCSS CLI + +Add [PostCSS CLI] to your project: + +```bash +npm install postcss-cli @csstools/postcss-custom-selectors-import-export --save-dev +``` + +Use [PostCSS Custom Selectors Import/Export] in your `postcss.config.js` configuration file: + +```js +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); + +module.exports = { + plugins: [ + postcssCustomSelectorsImportExport(/* pluginOptions */) + ] +} +``` + +## PostCSS Load Config + +If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). + +```bash +npm install @csstools/postcss-custom-selectors-import-export --save-dev +``` + +`package.json`: + +```json +{ + "postcss": { + "plugins": { + "@csstools/postcss-custom-selectors-import-export": {} + } + } +} +``` + +`.postcssrc.json`: + +```json +{ + "plugins": { + "@csstools/postcss-custom-selectors-import-export": {} + } +} +``` + +_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-custom-selectors-import-export --save-dev +``` + +Use [PostCSS Custom Selectors Import/Export] 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: [ + [ + "@csstools/postcss-custom-selectors-import-export", + { + // Options + }, + ], + ], + }, + }, + }, + ], + }, + ], + }, +}; +``` + +## Create React App + +Add [React App Rewired] and [React App Rewire PostCSS] to your project: + +```bash +npm install react-app-rewired react-app-rewire-postcss @csstools/postcss-custom-selectors-import-export --save-dev +``` + +Use [React App Rewire PostCSS] and [PostCSS Custom Selectors Import/Export] in your +`config-overrides.js` file: + +```js +const reactAppRewirePostcss = require('react-app-rewire-postcss'); +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); + +module.exports = config => reactAppRewirePostcss(config, { + plugins: () => [ + postcssCustomSelectorsImportExport(/* pluginOptions */) + ] +}); +``` + +## 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-custom-selectors-import-export --save-dev +``` + +Use [PostCSS Custom Selectors Import/Export] in your `postcss.config.json` file: + +```json +{ + "plugins": [ + "@csstools/postcss-custom-selectors-import-export" + ] +} +``` + +```json5 +{ + "plugins": [ + [ + "@csstools/postcss-custom-selectors-import-export", + { + // Optionally add plugin options + } + ] + ] +} +``` + +## Gulp + +Add [Gulp PostCSS] to your project: + +```bash +npm install gulp-postcss @csstools/postcss-custom-selectors-import-export --save-dev +``` + +Use [PostCSS Custom Selectors Import/Export] in your Gulpfile: + +```js +const postcss = require('gulp-postcss'); +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); + +gulp.task('css', function () { + var plugins = [ + postcssCustomSelectorsImportExport(/* 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-custom-selectors-import-export --save-dev +``` + +Use [PostCSS Custom Selectors Import/Export] in your Gruntfile: + +```js +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); + +grunt.loadNpmTasks('grunt-postcss'); + +grunt.initConfig({ + postcss: { + options: { + processors: [ + postcssCustomSelectorsImportExport(/* 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 Custom Selectors Import/Export]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-selectors-import-export +[React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss +[React App Rewired]: https://github.com/timarney/react-app-rewired +[Next.js]: https://nextjs.org diff --git a/plugins/postcss-custom-selectors-import-export/LICENSE.md b/plugins/postcss-custom-selectors-import-export/LICENSE.md new file mode 100644 index 000000000..6d7047088 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/LICENSE.md @@ -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. diff --git a/plugins/postcss-custom-selectors-import-export/README.md b/plugins/postcss-custom-selectors-import-export/README.md new file mode 100644 index 000000000..c9dc76703 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/README.md @@ -0,0 +1,128 @@ +# PostCSS Custom Selectors Import/Export [PostCSS Logo][PostCSS] + +[npm version][npm-url] [Build Status][cli-url] [Discord][discord] + +[PostCSS Custom Selectors Import/Export] lets you import or export `@custom-selector`'s into or out of your CSS. + +## Usage + +Add [PostCSS Custom Selectors Import/Export] to your project: + +```bash +npm install postcss @csstools/postcss-custom-selectors-import-export --save-dev +``` + +Use it as a [PostCSS] plugin: + +```js +const postcss = require('postcss'); +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); + +postcss([ + postcssCustomSelectorsImportExport(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +[PostCSS Custom Selectors Import/Export] 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) +- [Create React App](INSTALL.md#create-react-app) +- [Next.js](INSTALL.md#nextjs) +- [Gulp](INSTALL.md#gulp) +- [Grunt](INSTALL.md#grunt) + +## Options + +### 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 +postcssCustomSelectorsImportExport({ + importFrom: 'path/to/file.css' // => @custom-selector :--heading h1, h2, h3; +}); +``` + +```pcss +article :--heading + p { + margin-top: 0; +} + +/* becomes */ + +@custom-selector :--heading h1, h2, h3; +article :--heading+p { + margin-top: 0; +} +``` + +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 +postcssCustomSelectorsImportExport({ + 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 +postcssCustomSelectorsImportExport({ + 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: {} }; + +postcssCustomSelectorsImportExport({ + 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-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test + +[discord]: https://discord.gg/bUadyRwkJS +[npm-url]: https://www.npmjs.com/package/@csstools/postcss-custom-selectors-import-export + +[PostCSS]: https://github.com/postcss/postcss +[PostCSS Custom Selectors Import/Export]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-selectors-import-export diff --git a/plugins/postcss-custom-selectors-import-export/docs/README.md b/plugins/postcss-custom-selectors-import-export/docs/README.md new file mode 100644 index 000000000..789ba2a59 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/docs/README.md @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + +
+ +[] lets you import or export `@custom-selector`'s into or out of your CSS. + + + + + +## Options + +### 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 +({ + importFrom: 'path/to/file.css' // => @custom-selector :--heading h1, h2, h3; +}); +``` + +```pcss +article :--heading + p { + margin-top: 0; +} + +/* becomes */ + +@custom-selector :--heading h1, h2, h3; +article :--heading+p { + margin-top: 0; +} +``` + +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 +({ + 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 +({ + 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: {} }; + +({ + 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' } + } + ] +}); +``` + + diff --git a/plugins/postcss-custom-selectors-import-export/package.json b/plugins/postcss-custom-selectors-import-export/package.json new file mode 100644 index 000000000..cd1034639 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/package.json @@ -0,0 +1,96 @@ +{ + "name": "@csstools/postcss-custom-selectors-import-export", + "description": "Import/Export Custom Selectors in CSS", + "version": "1.0.0", + "contributors": [ + { + "name": "Antonio Laguna", + "email": "antonio@laguna.es", + "url": "https://antonio.laguna.es" + }, + { + "name": "Romain Menke", + "email": "romainmenke@gmail.com" + }, + { + "name": "Jonathan Neal", + "email": "jonathantneal@hotmail.com" + }, + { + "name": "Maxime Thirouin" + }, + { + "name": "yisi" + } + ], + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs", + "default": "./dist/index.mjs" + } + }, + "files": [ + "CHANGELOG.md", + "LICENSE.md", + "README.md", + "dist" + ], + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "devDependencies": { + "postcss-custom-selectors": "^7.0.0" + }, + "peerDependencies": { + "postcss": "^8.4" + }, + "scripts": { + "build": "rollup -c ../../rollup/default.mjs", + "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", + "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", + "lint": "npm run lint:eslint && npm run lint:package-json", + "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", + "test": "node .tape.cjs && node .tape.mjs && npm run test:exports", + "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs", + "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs && REWRITE_EXPECTS=true node .tape.cjs" + }, + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-selectors-import-export#readme", + "repository": { + "type": "git", + "url": "https://github.com/csstools/postcss-plugins.git", + "directory": "plugins/postcss-custom-selectors-import-export" + }, + "bugs": "https://github.com/csstools/postcss-plugins/issues", + "keywords": [ + "at-rule", + "atrule", + "css", + "custom", + "declarative", + "extensions", + "postcss", + "postcss-plugin", + "rule", + "selectors" + ], + "csstools": { + "exportName": "postcssCustomSelectorsImportExport", + "humanReadableName": "PostCSS Custom Selectors Import/Export" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/plugins/postcss-custom-selectors-import-export/src/custom-selectors-from-root.js b/plugins/postcss-custom-selectors-import-export/src/custom-selectors-from-root.js new file mode 100644 index 000000000..12579e384 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/src/custom-selectors-from-root.js @@ -0,0 +1,32 @@ +import parser from 'postcss-selector-parser'; + +// return custom selectors from the css root, conditionally removing them +export default (root) => { + // initialize custom selectors + const customSelectors = {}; + + // for each custom selector atrule that is a child of the css root + root.walkAtRules((node) => { + if (node.name.toLowerCase() !== 'custom-selector') { + return; + } + + if (!node.params || !node.params.includes(':--')) { + return; + } + + const source = node.params.trim(); + + const selectorAST = parser().astSync(source); + const nameNode = selectorAST?.nodes?.[0]?.nodes?.[0]; + if (!nameNode || nameNode.type !== 'pseudo' || !nameNode.value.startsWith(':--')) { + return; + } + + const name = nameNode.toString(); + + customSelectors[name] = source.slice(name.length).trim(); + }); + + return customSelectors; +}; diff --git a/plugins/postcss-custom-selectors-import-export/src/export-to.js b/plugins/postcss-custom-selectors-import-export/src/export-to.js new file mode 100644 index 000000000..3420fe074 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/src/export-to.js @@ -0,0 +1,129 @@ +import fs from 'fs'; +import path from 'path'; + +/* 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, '\t'); + 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 +/* ========================================================================== */ + +export default 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'); diff --git a/plugins/postcss-custom-selectors-import-export/src/import-from.js b/plugins/postcss-custom-selectors-import-export/src/import-from.js new file mode 100644 index 000000000..72fa214dd --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/src/import-from.js @@ -0,0 +1,125 @@ +import fs from 'fs'; +import path from 'path'; +import url from 'url'; +import postcss from 'postcss'; +import getCustomSelectors from './custom-selectors-from-root'; + +/* 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(url.pathToFileURL(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'], + ); + + return customSelectors; +} + +/* Import Custom Selectors from JSON file +/* ========================================================================== */ + +async function importCustomSelectorsFromJSONFile(from) { + const object = await readJSON(url.pathToFileURL(path.resolve(from))); + + return importCustomSelectorsFromObject(object); +} + +/* Import Custom Selectors from JS file +/* ========================================================================== */ + +async function importCustomSelectorsFromJSFile(from) { + const object = await import(url.pathToFileURL(path.resolve(from))); + + if ('default' in object) { + return importCustomSelectorsFromObject(object.default); + } + + return importCustomSelectorsFromObject(object); +} + +/* Import Custom Selectors from Sources +/* ========================================================================== */ + +export default 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 === 'mjs') { + 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)); diff --git a/plugins/postcss-custom-selectors-import-export/src/index.js b/plugins/postcss-custom-selectors-import-export/src/index.js new file mode 100644 index 000000000..a6d27d0c7 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/src/index.js @@ -0,0 +1,77 @@ +import getCustomSelectors from './custom-selectors-from-root'; +import importCustomSelectorsFromSources from './import-from'; +import exportCustomSelectorsToDestinations from './export-to'; + +const creator = (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-import-export', + Once: async (root, { AtRule }) => { + const importedSelectors = await customSelectorsPromise; + + const allCustomSelectors = Object.assign( + {}, + importedSelectors, + getCustomSelectors(root, { preserve }), + ); + + await exportCustomSelectorsToDestinations(allCustomSelectors, exportTo); + + if (importedSelectors) { + const selectorNames = Object.keys(importedSelectors); + selectorNames.reverse(); + + const lastCustomSelector = root.nodes.find((node) => { + return node.type === 'atrule' && node.name === 'custom-selector'; + }); + + if (lastCustomSelector) { + selectorNames.forEach((selectorName) => { + lastCustomSelector.before(new AtRule({ + name: 'custom-selector', + params: `${selectorName} ${importedSelectors[selectorName].toString()}`, + source: { + input: { + from: root.input?.from ?? 'postcss-custom-selectors-import-export', + }, + start: { line: 1, column: 1 }, + end: { line: 1, column: 1 }, + }, + })); + }); + + return; + } + + selectorNames.forEach((selectorName) => { + root.prepend(new AtRule({ + name: 'custom-selector', + params: `${selectorName} ${importedSelectors[selectorName].toString()}`, + source: { + input: { + from: root.input?.from ?? 'postcss-custom-selectors-import-export', + }, + start: { line: 1, column: 1 }, + end: { line: 1, column: 1 }, + }, + })); + }); + } + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-custom-selectors-import-export/test/_import.mjs b/plugins/postcss-custom-selectors-import-export/test/_import.mjs new file mode 100644 index 000000000..3b174fff6 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/_import.mjs @@ -0,0 +1,6 @@ +import assert from 'assert'; +import plugin from '@csstools/postcss-custom-selectors-import-export'; +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); diff --git a/plugins/postcss-custom-selectors-import-export/test/_require.cjs b/plugins/postcss-custom-selectors-import-export/test/_require.cjs new file mode 100644 index 000000000..30faeabc0 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/_require.cjs @@ -0,0 +1,6 @@ +const assert = require('assert'); +const plugin = require('@csstools/postcss-custom-selectors-import-export'); +plugin(); + +assert.ok(plugin.postcss, 'should have "postcss flag"'); +assert.equal(typeof plugin, 'function', 'should return a function'); diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css new file mode 100644 index 000000000..23fdd405c --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css @@ -0,0 +1,3 @@ +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css new file mode 100644 index 000000000..23fdd405c --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css @@ -0,0 +1,3 @@ +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css new file mode 100644 index 000000000..f4b1e7127 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css @@ -0,0 +1,5 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; +article :--heading+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css new file mode 100644 index 000000000..e5852b189 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css @@ -0,0 +1,3 @@ +article :is(h1, h2, h3)+p { + margin-top: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.css b/plugins/postcss-custom-selectors-import-export/test/basic.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-css-to-type.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-css-to-type.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-css-to-type.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-css-to.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-css-to.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-css-to.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-css.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-css.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-css.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-fn-promise.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-fn-promise.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-fn-promise.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-fn.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-fn.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-fn.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-js.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-js.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-js.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-json.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-json.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-json.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export-mjs.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export-mjs.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export-mjs.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic.export.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic.export.expect.css new file mode 100644 index 000000000..e3ce21368 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic.export.expect.css @@ -0,0 +1,126 @@ +@custom-selector :--foo .bar, .baz; + +.foo:--foo { + margin-top: 16px; +} + +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; + +:--any-heading + p {} + +@custom-selector :--foobar .foo, .bar; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types + .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; + +:--foobar > :--baz {} +:--fizzbuzz > :--foobar {} +:--button-types, :--button-types:active {} + +@custom-selector :--commented-foo + /* comment */ + .foo, + .bar > .baz; + +:--commented-foo + p { + display: block; +} + +@custom-selector :--pseudo ::before, ::after; + +.foo > a:--pseudo img { + display: block; +} + +@custom-selector :--foo .foo; + +:--foo, :--foo.bar { + color: white; +} + +:--foo :--foo:hover { + color: white; +} + +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; + +.fo--oo > :--fo-----o { + margin: auto; +} + +:--ba-----r:hover .ba--z { + display: block; +} + +/* comment */ + +article :--heading + p { + margin-top: 0; +} + +@custom-selector :--multiline + .foo, + .bar > .baz; + +:--multiline { + display: block; +} + +/* should works with collapsed custom selectors */ + +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; + +:--button:--enter {} + +@custom-selector :--any-foobar .foo, .bar; + +:--any-foobar h1 { + margin-top: 16px; +} + +main :--foo + p { + margin-top: 16px; +} + +@custom-selector :--foobar .foo; + +:--foobar { + order: 1000; +} +a, :--foobar { + order: 1001; +} +b,:--foobar { + order: 1002; +} + +@custom-selector :--foobaz .foo.baz; + +:--foobaz { + order: 1010; +} +a, :--foobaz { + order: 1011; +} +b,:--foobaz { + order: 1012; +} + +@custom-selector :--foobazz .foo .baz; + +:--foobazz { + order: 1020; +} +a, :--foobazz { + order: 1021; +} +b,:--foobazz { + order: 1022; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/empty.css b/plugins/postcss-custom-selectors-import-export/test/empty.css new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/postcss-custom-selectors-import-export/test/examples/.gitkeep b/plugins/postcss-custom-selectors-import-export/test/examples/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/postcss-custom-selectors-import-export/test/export-selectors.css b/plugins/postcss-custom-selectors-import-export/test/export-selectors.css new file mode 100644 index 000000000..b129f1329 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/export-selectors.css @@ -0,0 +1,22 @@ +@custom-selector :--foo .foo; +@custom-selector :--any-heading h1, h2, h3, h4, h5, h6; +@custom-selector :--foobar .foo; +@custom-selector :--baz .baz; +@custom-selector :--fizzbuzz .fizz, .buzz; +@custom-selector :--button-types .btn-primary, + .btn-success, + .btn-info, + .btn-warning, + .btn-danger; +@custom-selector :--commented-foo .foo, + .bar > .baz; +@custom-selector :--pseudo ::before, ::after; +@custom-selector :--fo-----o h1, h2, h3; +@custom-selector :--ba-----r h4, h5, h6; +@custom-selector :--multiline .foo, + .bar > .baz; +@custom-selector :--button button, .button; +@custom-selector :--enter :hover, :focus; +@custom-selector :--any-foobar .foo, .bar; +@custom-selector :--foobaz .foo.baz; +@custom-selector :--foobazz .foo .baz; diff --git a/plugins/postcss-custom-selectors-import-export/test/export-selectors.js b/plugins/postcss-custom-selectors-import-export/test/export-selectors.js new file mode 100644 index 000000000..060a3658a --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/export-selectors.js @@ -0,0 +1,20 @@ +module.exports = { + customSelectors: { + ':--foo': '.foo', + ':--any-heading': 'h1, h2, h3, h4, h5, h6', + ':--foobar': '.foo', + ':--baz': '.baz', + ':--fizzbuzz': '.fizz, .buzz', + ':--button-types': '.btn-primary,\n .btn-success,\n .btn-info,\n .btn-warning,\n .btn-danger', + ':--commented-foo': '.foo,\n .bar > .baz', + ':--pseudo': '::before, ::after', + ':--fo-----o': 'h1, h2, h3', + ':--ba-----r': 'h4, h5, h6', + ':--multiline': '.foo,\n .bar > .baz', + ':--button': 'button, .button', + ':--enter': ':hover, :focus', + ':--any-foobar': '.foo, .bar', + ':--foobaz': '.foo.baz', + ':--foobazz': '.foo .baz' + } +}; diff --git a/plugins/postcss-custom-selectors-import-export/test/export-selectors.json b/plugins/postcss-custom-selectors-import-export/test/export-selectors.json new file mode 100644 index 000000000..c7967c5e9 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/export-selectors.json @@ -0,0 +1,20 @@ +{ + "custom-selectors": { + ":--foo": ".foo", + ":--any-heading": "h1, h2, h3, h4, h5, h6", + ":--foobar": ".foo", + ":--baz": ".baz", + ":--fizzbuzz": ".fizz, .buzz", + ":--button-types": ".btn-primary,\n\t.btn-success,\n\t.btn-info,\n\t.btn-warning,\n\t.btn-danger", + ":--commented-foo": ".foo,\n\t.bar > .baz", + ":--pseudo": "::before, ::after", + ":--fo-----o": "h1, h2, h3", + ":--ba-----r": "h4, h5, h6", + ":--multiline": ".foo,\n\t.bar > .baz", + ":--button": "button, .button", + ":--enter": ":hover, :focus", + ":--any-foobar": ".foo, .bar", + ":--foobaz": ".foo.baz", + ":--foobazz": ".foo .baz" + } +} diff --git a/plugins/postcss-custom-selectors-import-export/test/export-selectors.mjs b/plugins/postcss-custom-selectors-import-export/test/export-selectors.mjs new file mode 100644 index 000000000..a73ac9bc5 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/export-selectors.mjs @@ -0,0 +1,18 @@ +export const customSelectors = { + ':--foo': '.foo', + ':--any-heading': 'h1, h2, h3, h4, h5, h6', + ':--foobar': '.foo', + ':--baz': '.baz', + ':--fizzbuzz': '.fizz, .buzz', + ':--button-types': '.btn-primary,\n .btn-success,\n .btn-info,\n .btn-warning,\n .btn-danger', + ':--commented-foo': '.foo,\n .bar > .baz', + ':--pseudo': '::before, ::after', + ':--fo-----o': 'h1, h2, h3', + ':--ba-----r': 'h4, h5, h6', + ':--multiline': '.foo,\n .bar > .baz', + ':--button': 'button, .button', + ':--enter': ':hover, :focus', + ':--any-foobar': '.foo, .bar', + ':--foobaz': '.foo.baz', + ':--foobazz': '.foo .baz' +}; diff --git a/plugins/postcss-custom-selectors-import-export/test/import-selectors.css b/plugins/postcss-custom-selectors-import-export/test/import-selectors.css new file mode 100644 index 000000000..ef3ea5baf --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/import-selectors.css @@ -0,0 +1,2 @@ +@custom-selector :--heading h1, h2, h3; +@custom-selector :--text :--heading, p; diff --git a/plugins/postcss-custom-selectors-import-export/test/import-selectors.js b/plugins/postcss-custom-selectors-import-export/test/import-selectors.js new file mode 100644 index 000000000..47cdbc57a --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/import-selectors.js @@ -0,0 +1,6 @@ +module.exports = { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + }, +}; diff --git a/plugins/postcss-custom-selectors-import-export/test/import-selectors.json b/plugins/postcss-custom-selectors-import-export/test/import-selectors.json new file mode 100644 index 000000000..5a162f940 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/import-selectors.json @@ -0,0 +1,6 @@ +{ + "custom-selectors": { + ":--heading": "h1, h2, h3", + ":--text": ":--heading, p" + } +} diff --git a/plugins/postcss-custom-selectors-import-export/test/import-selectors.mjs b/plugins/postcss-custom-selectors-import-export/test/import-selectors.mjs new file mode 100644 index 000000000..c989ccae2 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/import-selectors.mjs @@ -0,0 +1,6 @@ +export default { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + }, +}; diff --git a/rollup/presets/package-javascript.mjs b/rollup/presets/package-javascript.mjs index 986f3e0a9..2c852e52d 100644 --- a/rollup/presets/package-javascript.mjs +++ b/rollup/presets/package-javascript.mjs @@ -18,13 +18,13 @@ export function packageJavascript() { exclude: 'node_modules/**', presets: packageBabelPreset, }), - terser({ - compress: { - reduce_funcs: false, // https://github.com/terser/terser/issues/1305 - }, - keep_classnames: true, - keep_fnames: true, - }), + // terser({ + // compress: { + // reduce_funcs: false, // https://github.com/terser/terser/issues/1305 + // }, + // keep_classnames: true, + // keep_fnames: true, + // }), ], }, ]; From 0270757819a701e9f7332bbac7738ddc9ec4254b Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Tue, 22 Nov 2022 19:30:28 +0100 Subject: [PATCH 2/3] wip --- .github/ISSUE_TEMPLATE/css-issue.yml | 3 + .github/ISSUE_TEMPLATE/plugin-issue.yml | 3 + .github/labeler.yml | 8 + package-lock.json | 28 ++- .../package.json | 24 +-- .../.tape.cjs | 8 +- .../.tape.mjs | 69 +----- .../INSTALL.md | 77 ++++--- .../README.md | 199 ++++++------------ .../docs/README.md | 125 +++++++++++ .../package.json | 47 +++-- .../src/index.ts | 124 ++++++----- .../lib/get-custom-properties-from-imports.ts | 34 ++- .../lib/get-custom-properties-from-root.ts | 28 +-- .../src/lib/is-ignored.ts | 20 -- .../src/lib/transform-properties.ts | 86 -------- .../src/lib/transform-value-ast.ts | 50 ----- .../lib/write-custom-properties-to-exports.ts | 4 +- .../test/_import.mjs | 2 +- .../test/_require.cjs | 2 +- .../test/basic.expect.css | 23 -- .../test/basic.export-css-to-type.expect.css | 160 ++++++++++++++ .../test/basic.export-css-to.expect.css | 160 ++++++++++++++ .../test/basic.export-css.expect.css | 160 ++++++++++++++ .../test/basic.export-fn-promise.expect.css | 160 ++++++++++++++ .../test/basic.export-fn.expect.css | 160 ++++++++++++++ .../test/basic.export-js.expect.css | 160 ++++++++++++++ .../test/basic.export-json.expect.css | 160 ++++++++++++++ .../test/basic.export-mjs.expect.css | 160 ++++++++++++++ .../test/basic.export-scss.expect.css | 160 ++++++++++++++ .../test/basic.export.expect.css | 160 ++++++++++++++ .../test/basic.import-cjs.expect.css | 167 +++++++++++++++ .../basic.import-css-from-type.expect.css | 167 +++++++++++++++ .../test/basic.import-css-from.expect.css | 167 +++++++++++++++ .../test/basic.import-css-js.expect.css | 167 +++++++++++++++ .../test/basic.import-css-pcss.expect.css | 167 +++++++++++++++ .../test/basic.import-css.expect.css | 167 +++++++++++++++ .../test/basic.import-fn-promise.expect.css | 167 +++++++++++++++ .../test/basic.import-fn.expect.css | 168 +++++++++++++++ .../test/basic.import-is-empty.expect.css | 23 -- .../test/basic.import-json.expect.css | 167 +++++++++++++++ .../test/basic.import-mjs.expect.css | 167 +++++++++++++++ .../test/basic.import-override.expect.css | 88 +++++--- .../basic.import-override.inverse.expect.css | 88 +++++--- .../test/basic.import.expect.css | 32 +-- .../test/basic.preserve.expect.css | 77 +++++-- .../test/examples/.gitkeep | 0 .../test/import.expect.css | 1 - .../.tape.cjs | 34 +++ .../.tape.mjs | 34 +++ .../README.md | 29 ++- .../docs/README.md | 29 ++- .../src/import-from.js | 2 +- .../src/index.js | 56 +++-- .../test/basic-import.css | 7 + ...-import.css-from-multiple-files.expect.css | 9 + .../basic-import.css-from-type.expect.css | 9 + .../test/basic-import.css-from.expect.css | 9 + .../test/basic-import.css.expect.css | 9 + .../test/basic-import.empty.expect.css | 7 + .../test/basic-import.expect.css | 9 + .../test/basic-import.fn-promise.expect.css | 9 + .../test/basic-import.fn.expect.css | 9 + .../test/basic-import.js.expect.css | 9 + .../test/basic-import.json.expect.css | 9 + .../test/basic-import.mjs.expect.css | 9 + ...ide-import-from-with-root.false.expect.css | 14 ++ ...ride-import-from-with-root.true.expect.css | 14 ++ ...sic-import.with-polyfill-plugin.expect.css | 4 + 69 files changed, 4131 insertions(+), 733 deletions(-) create mode 100644 plugins/postcss-custom-properties-import-export/docs/README.md delete mode 100644 plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts delete mode 100644 plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts delete mode 100644 plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.export.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css create mode 100644 plugins/postcss-custom-properties-import-export/test/examples/.gitkeep create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.false.expect.css create mode 100644 plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.true.expect.css diff --git a/.github/ISSUE_TEMPLATE/css-issue.yml b/.github/ISSUE_TEMPLATE/css-issue.yml index 135995ae7..8107c8b73 100644 --- a/.github/ISSUE_TEMPLATE/css-issue.yml +++ b/.github/ISSUE_TEMPLATE/css-issue.yml @@ -70,8 +70,11 @@ body: - PostCSS Color Hex Alpha - PostCSS Color Mod Function - PostCSS Custom Media Queries + - PostCSS Custom Media Queries Import/Export - PostCSS Custom Properties + - PostCSS Custom Properties Import/Export - PostCSS Custom Selectors + - PostCSS Custom Selectors Import/Export - PostCSS Design Tokens - PostCSS Dir Pseudo Class - PostCSS Double Position Gradients diff --git a/.github/ISSUE_TEMPLATE/plugin-issue.yml b/.github/ISSUE_TEMPLATE/plugin-issue.yml index 2fa9f2915..fa97f0a38 100644 --- a/.github/ISSUE_TEMPLATE/plugin-issue.yml +++ b/.github/ISSUE_TEMPLATE/plugin-issue.yml @@ -72,8 +72,11 @@ body: - PostCSS Color Hex Alpha - PostCSS Color Mod Function - PostCSS Custom Media Queries + - PostCSS Custom Media Queries Import/Export - PostCSS Custom Properties + - PostCSS Custom Properties Import/Export - PostCSS Custom Selectors + - PostCSS Custom Selectors Import/Export - PostCSS Design Tokens - PostCSS Dir Pseudo Class - PostCSS Double Position Gradients diff --git a/.github/labeler.yml b/.github/labeler.yml index c874c1372..90ae46a52 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -216,5 +216,13 @@ - plugins/postcss-trigonometric-functions/** - experimental/postcss-trigonometric-functions/** +"plugins/import-export": + - plugins/postcss-custom-media-queries-import-export/** + - experimental/postcss-custom-media-queries-import-export/** + - plugins/postcss-custom-properties-import-export/** + - experimental/postcss-custom-properties-import-export/** + - plugins/postcss-custom-selectors-import-export/** + - experimental/postcss-custom-selectors-import-export/** + "sites/postcss-preset-env": - sites/postcss-preset-env/** diff --git a/package-lock.json b/package-lock.json index 1b9d9ab7e..47a4b45d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7406,11 +7406,14 @@ }, "plugins/postcss-custom-media-import-export": { "name": "@csstools/postcss-custom-media-import-export", - "version": "8.0.2", + "version": "1.0.0", "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, + "devDependencies": { + "postcss-custom-media": "^9.0.1" + }, "engines": { "node": "^12 || ^14 || >=16" }, @@ -7444,23 +7447,21 @@ }, "plugins/postcss-custom-properties-import-export": { "name": "@csstools/postcss-custom-properties-import-export", - "version": "12.1.10", + "version": "1.0.0", "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, "devDependencies": { + "postcss-custom-properties": "^13.0.0", "postcss-import": "^15.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "plugins/postcss-custom-selectors": { @@ -7487,15 +7488,18 @@ "dependencies": { "postcss-selector-parser": "^6.0.4" }, + "devDependencies": { + "postcss-custom-selectors": "^7.0.0" + }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "plugins/postcss-design-tokens": { @@ -9354,19 +9358,21 @@ "@csstools/postcss-custom-media-import-export": { "version": "file:plugins/postcss-custom-media-import-export", "requires": { + "postcss-custom-media": "^9.0.1", "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-custom-properties-import-export": { "version": "file:plugins/postcss-custom-properties-import-export", "requires": { - "postcss-import": "^15.0.0", - "postcss-value-parser": "^4.2.0" + "postcss-custom-properties": "^13.0.0", + "postcss-import": "^15.0.0" } }, "@csstools/postcss-custom-selectors-import-export": { "version": "file:plugins/postcss-custom-selectors-import-export", "requires": { + "postcss-custom-selectors": "^7.0.0", "postcss-selector-parser": "^6.0.4" } }, diff --git a/plugins/postcss-custom-media-import-export/package.json b/plugins/postcss-custom-media-import-export/package.json index cdc1acee4..0b35a255f 100644 --- a/plugins/postcss-custom-media-import-export/package.json +++ b/plugins/postcss-custom-media-import-export/package.json @@ -1,7 +1,7 @@ { "name": "@csstools/postcss-custom-media-import-export", - "description": "Use Custom Media Queries in CSS", - "version": "8.0.2", + "description": "Import/Export Custom Media Queries in CSS", + "version": "1.0.0", "contributors": [ { "name": "Antonio Laguna", @@ -47,11 +47,14 @@ "dependencies": { "postcss-value-parser": "^4.2.0" }, + "devDependencies": { + "postcss-custom-media": "^9.0.1" + }, "peerDependencies": { "postcss": "^8.2" }, "scripts": { - "build": "rollup -c ../../rollup/default.js", + "build": "rollup -c ../../rollup/default.mjs", "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", "lint": "npm run lint:eslint && npm run lint:package-json", @@ -62,32 +65,27 @@ "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs", "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.cjs" }, - "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media#readme", + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media-import-export#readme", "repository": { "type": "git", "url": "https://github.com/csstools/postcss-plugins.git", - "directory": "plugins/postcss-custom-media" + "directory": "plugins/postcss-custom-media-import-export" }, "bugs": "https://github.com/csstools/postcss-plugins/issues", "keywords": [ "at-rule", "atrule", "css", - "csswg", "custom", "media", "postcss", "postcss-plugin", "queries", - "query", - "specification", - "w3c" + "query" ], "csstools": { - "cssdbId": "custom-media-queries", - "exportName": "postcssCustomMedia", - "humanReadableName": "PostCSS Custom Media", - "specUrl": "https://www.w3.org/TR/mediaqueries-5/#at-ruledef-custom-media" + "exportName": "postcssCustomMediaImportExport", + "humanReadableName": "PostCSS Custom Media Import/Export" }, "volta": { "extends": "../../package.json" diff --git a/plugins/postcss-custom-properties-import-export/.tape.cjs b/plugins/postcss-custom-properties-import-export/.tape.cjs index 3c717ab09..166d4f387 100644 --- a/plugins/postcss-custom-properties-import-export/.tape.cjs +++ b/plugins/postcss-custom-properties-import-export/.tape.cjs @@ -1,29 +1,23 @@ const postcssTape = require('../../packages/postcss-tape/dist/index.cjs'); -const plugin = require('postcss-custom-properties'); +const plugin = require('@csstools/postcss-custom-properties-import-export'); postcssTape(plugin)({ 'basic:import-cjs': { message: 'supports { importFrom: "test/import-properties{-2}?.cjs" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.cjs', 'test/import-properties-2.cjs' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-css-js': { message: 'supports { importFrom: "test/import-properties{-2}?.{css|js}" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.js', 'test/import-properties-2.css' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' } }); diff --git a/plugins/postcss-custom-properties-import-export/.tape.mjs b/plugins/postcss-custom-properties-import-export/.tape.mjs index 43fa4ae48..dfa935e93 100644 --- a/plugins/postcss-custom-properties-import-export/.tape.mjs +++ b/plugins/postcss-custom-properties-import-export/.tape.mjs @@ -1,5 +1,5 @@ import postcssTape from '../../packages/postcss-tape/dist/index.mjs'; -import plugin from 'postcss-custom-properties'; +import plugin from '@csstools/postcss-custom-properties-import-export'; import { strict as assert } from 'assert'; import postcssImport from 'postcss-import'; import fs from 'fs'; @@ -16,7 +16,6 @@ postcssTape(plugin)({ }, 'basic:import': { message: 'supports { importFrom: { customProperties: { ... } } } usage', - warnings: 1, options: { importFrom: { customProperties: { @@ -31,7 +30,6 @@ postcssTape(plugin)({ }, 'basic:import-fn': { message: 'supports { importFrom() } usage', - warnings: 1, options: { importFrom() { return { @@ -45,12 +43,9 @@ postcssTape(plugin)({ }; } }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-fn-promise': { message: 'supports { async importFrom() } usage', - warnings: 1, options: { importFrom() { return new Promise(resolve => { @@ -65,105 +60,78 @@ postcssTape(plugin)({ }); } }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-json': { message: 'supports { importFrom: "test/import-properties.json" } usage', - warnings: 1, options: { importFrom: 'test/import-properties.json' }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-cjs': { message: 'supports { importFrom: "test/import-properties{-2}?.cjs" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.cjs', 'test/import-properties-2.cjs' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-mjs': { message: 'supports { importFrom: "test/import-properties{-2}?.mjs" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.mjs', 'test/import-properties-2.mjs' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-css': { message: 'supports { importFrom: "test/import-properties{-2}?.css" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.css', 'test/import-properties-2.css' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-css-js': { message: 'supports { importFrom: "test/import-properties{-2}?.{css|js}" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.js', 'test/import-properties-2.css' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-css-pcss': { message: 'supports { importFrom: "test/import-properties.{p}?css" } usage', - warnings: 1, options: { importFrom: [ 'test/import-properties.pcss', 'test/import-properties-2.css' ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-css-from': { message: 'supports { importFrom: { from: "test/import-properties.css" } } usage', - warnings: 1, options: { importFrom: [ { from: 'test/import-properties.css' }, { from: 'test/import-properties-2.css' } ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-css-from-type': { message: 'supports { importFrom: [ { from: "test/import-properties.css", type: "css" } ] } usage', - warnings: 1, options: { importFrom: [ { from: 'test/import-properties.css', type: 'css' }, { from: 'test/import-properties-2.css', type: 'css' } ] }, - expect: 'basic.import.expect.css', - result: 'basic.import.result.css' }, 'basic:import-override': { message: 'importFrom with { preserve: false } should override root properties', - warnings: 1, options: { preserve: false, importFrom: { @@ -177,12 +145,9 @@ postcssTape(plugin)({ } } }, - expect: 'basic.import-override.expect.css', - result: 'basic.import-override.result.css' }, 'basic:import-override:inverse': { message: 'importFrom with { preserve: false, overrideImportFromWithRoot: true } should override importFrom properties', - warnings: 1, options: { preserve: false, overrideImportFromWithRoot: true, @@ -197,19 +162,14 @@ postcssTape(plugin)({ } } }, - expect: 'basic.import-override.inverse.expect.css', - result: 'basic.import-override.inverse.result.css' }, 'basic:export': { message: 'supports { exportTo: { customProperties: { ... } } } usage', - warnings: 1, options: { exportTo: (global.__exportPropertiesObject = global.__exportPropertiesObject || { customProperties: null }) }, - expect: 'basic.expect.css', - result: 'basic.result.css', after() { if (__exportPropertiesObject.customProperties['--color'] !== 'rgb(255, 0, 0)') { throw new Error('The exportTo function failed'); @@ -218,7 +178,6 @@ postcssTape(plugin)({ }, 'basic:export-fn': { message: 'supports { exportTo() } usage', - warnings: 1, options: { exportTo(customProperties) { if (customProperties['--color'] !== 'rgb(255, 0, 0)') { @@ -226,12 +185,9 @@ postcssTape(plugin)({ } } }, - expect: 'basic.expect.css', - result: 'basic.result.css' }, 'basic:export-fn-promise': { message: 'supports { async exportTo() } usage', - warnings: 1, options: { exportTo(customProperties) { return new Promise((resolve, reject) => { @@ -243,17 +199,12 @@ postcssTape(plugin)({ }); } }, - expect: 'basic.expect.css', - result: 'basic.result.css' }, 'basic:export-scss': { message: 'supports { exportTo: "test/export-properties.scss" } usage', - warnings: 1, options: { exportTo: 'test/export-properties.scss' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.scss', 'utf8'); @@ -268,12 +219,9 @@ postcssTape(plugin)({ }, 'basic:export-json': { message: 'supports { exportTo: "test/export-properties.json" } usage', - warnings: 1, options: { exportTo: 'test/export-properties.json' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.json', 'utf8'); @@ -288,12 +236,9 @@ postcssTape(plugin)({ }, 'basic:export-js': { message: 'supports { exportTo: "test/export-properties.js" } usage', - warnings: 1, options: { exportTo: 'test/export-properties.js' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.js', 'utf8'); @@ -308,12 +253,9 @@ postcssTape(plugin)({ }, 'basic:export-mjs': { message: 'supports { exportTo: "test/export-properties.mjs" } usage', - warnings: 1, options: { exportTo: 'test/export-properties.mjs' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.mjs', 'utf8'); @@ -328,12 +270,9 @@ postcssTape(plugin)({ }, 'basic:export-css': { message: 'supports { exportTo: "test/export-properties.css" } usage', - warnings: 1, options: { exportTo: 'test/export-properties.css' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.css', 'utf8'); @@ -348,12 +287,9 @@ postcssTape(plugin)({ }, 'basic:export-css-to': { message: 'supports { exportTo: { to: "test/export-properties.css" } } usage', - warnings: 1, options: { exportTo: { to: 'test/export-properties.css' } }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.css', 'utf8'); @@ -368,12 +304,9 @@ postcssTape(plugin)({ }, 'basic:export-css-to-type': { message: 'supports { exportTo: { to: "test/export-properties.css", type: "css" } } usage', - warnings: 1, options: { exportTo: { to: 'test/export-properties.css', type: 'css' } }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { try { global.__exportPropertiesString = fs.readFileSync('test/export-properties.css', 'utf8'); diff --git a/plugins/postcss-custom-properties-import-export/INSTALL.md b/plugins/postcss-custom-properties-import-export/INSTALL.md index 17e15cb65..b215f4bbf 100644 --- a/plugins/postcss-custom-properties-import-export/INSTALL.md +++ b/plugins/postcss-custom-properties-import-export/INSTALL.md @@ -1,6 +1,6 @@ -# Installing PostCSS Custom Properties +# Installing PostCSS Custom Properties Import/Export -[PostCSS Custom Properties] runs in all Node environments, with special instructions for: +[PostCSS Custom Properties Import/Export] runs in all Node environments, with special instructions for: - [Node](#node) - [PostCSS CLI](#postcss-cli) @@ -13,20 +13,31 @@ ## Node -Add [PostCSS Custom Properties] to your project: +Add [PostCSS Custom Properties Import/Export] to your project: ```bash -npm install postcss postcss-custom-properties --save-dev +npm install postcss @csstools/postcss-custom-properties-import-export --save-dev ``` Use it as a [PostCSS] plugin: ```js +// commonjs const postcss = require('postcss'); -const postcssCustomProperties = require('postcss-custom-properties'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); postcss([ - postcssCustomProperties(/* pluginOptions */) + postcssCustomPropertiesImportExport(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +```js +// esm +import postcss from 'postcss'; +import postcssCustomPropertiesImportExport from '@csstools/postcss-custom-properties-import-export'; + +postcss([ + postcssCustomPropertiesImportExport(/* pluginOptions */) ]).process(YOUR_CSS /*, processOptions */); ``` @@ -35,17 +46,17 @@ postcss([ Add [PostCSS CLI] to your project: ```bash -npm install postcss-cli postcss-custom-properties --save-dev +npm install postcss-cli @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [PostCSS Custom Properties] in your `postcss.config.js` configuration file: +Use [PostCSS Custom Properties Import/Export] in your `postcss.config.js` configuration file: ```js -const postcssCustomProperties = require('postcss-custom-properties'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); module.exports = { plugins: [ - postcssCustomProperties(/* pluginOptions */) + postcssCustomPropertiesImportExport(/* pluginOptions */) ] } ``` @@ -55,7 +66,7 @@ module.exports = { If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). ```bash -npm install postcss-custom-properties --save-dev +npm install @csstools/postcss-custom-properties-import-export --save-dev ``` `package.json`: @@ -64,7 +75,7 @@ npm install postcss-custom-properties --save-dev { "postcss": { "plugins": { - "postcss-custom-properties": {} + "@csstools/postcss-custom-properties-import-export": {} } } } @@ -75,7 +86,7 @@ npm install postcss-custom-properties --save-dev ```json { "plugins": { - "postcss-custom-properties": {} + "@csstools/postcss-custom-properties-import-export": {} } } ``` @@ -89,10 +100,10 @@ _Webpack version 5_ Add [PostCSS Loader] to your project: ```bash -npm install postcss-loader postcss-custom-properties --save-dev +npm install postcss-loader @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [PostCSS Custom Properties] in your Webpack configuration: +Use [PostCSS Custom Properties Import/Export] in your Webpack configuration: ```js module.exports = { @@ -112,7 +123,7 @@ module.exports = { postcssOptions: { plugins: [ [ - "postcss-custom-properties", + "@csstools/postcss-custom-properties-import-export", { // Options }, @@ -133,19 +144,19 @@ module.exports = { Add [React App Rewired] and [React App Rewire PostCSS] to your project: ```bash -npm install react-app-rewired react-app-rewire-postcss postcss-custom-properties --save-dev +npm install react-app-rewired react-app-rewire-postcss @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [React App Rewire PostCSS] and [PostCSS Custom Properties] in your +Use [React App Rewire PostCSS] and [PostCSS Custom Properties Import/Export] in your `config-overrides.js` file: ```js const reactAppRewirePostcss = require('react-app-rewire-postcss'); -const postcssCustomProperties = require('postcss-custom-properties'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); module.exports = config => reactAppRewirePostcss(config, { plugins: () => [ - postcssCustomProperties(/* pluginOptions */) + postcssCustomPropertiesImportExport(/* pluginOptions */) ] }); ``` @@ -155,15 +166,15 @@ module.exports = config => reactAppRewirePostcss(config, { 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 postcss-custom-properties --save-dev +npm install @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [PostCSS Custom Properties] in your `postcss.config.json` file: +Use [PostCSS Custom Properties Import/Export] in your `postcss.config.json` file: ```json { "plugins": [ - "postcss-custom-properties" + "@csstools/postcss-custom-properties-import-export" ] } ``` @@ -172,7 +183,7 @@ Use [PostCSS Custom Properties] in your `postcss.config.json` file: { "plugins": [ [ - "postcss-custom-properties", + "@csstools/postcss-custom-properties-import-export", { // Optionally add plugin options } @@ -186,18 +197,18 @@ Use [PostCSS Custom Properties] in your `postcss.config.json` file: Add [Gulp PostCSS] to your project: ```bash -npm install gulp-postcss postcss-custom-properties --save-dev +npm install gulp-postcss @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [PostCSS Custom Properties] in your Gulpfile: +Use [PostCSS Custom Properties Import/Export] in your Gulpfile: ```js const postcss = require('gulp-postcss'); -const postcssCustomProperties = require('postcss-custom-properties'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); gulp.task('css', function () { var plugins = [ - postcssCustomProperties(/* pluginOptions */) + postcssCustomPropertiesImportExport(/* pluginOptions */) ]; return gulp.src('./src/*.css') @@ -211,13 +222,13 @@ gulp.task('css', function () { Add [Grunt PostCSS] to your project: ```bash -npm install grunt-postcss postcss-custom-properties --save-dev +npm install grunt-postcss @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [PostCSS Custom Properties] in your Gruntfile: +Use [PostCSS Custom Properties Import/Export] in your Gruntfile: ```js -const postcssCustomProperties = require('postcss-custom-properties'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); grunt.loadNpmTasks('grunt-postcss'); @@ -225,7 +236,7 @@ grunt.initConfig({ postcss: { options: { processors: [ - postcssCustomProperties(/* pluginOptions */) + postcssCustomPropertiesImportExport(/* pluginOptions */) ] }, dist: { @@ -240,7 +251,7 @@ grunt.initConfig({ [PostCSS]: https://github.com/postcss/postcss [PostCSS CLI]: https://github.com/postcss/postcss-cli [PostCSS Loader]: https://github.com/postcss/postcss-loader -[PostCSS Custom Properties]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties +[PostCSS Custom Properties Import/Export]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties-import-export [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss [React App Rewired]: https://github.com/timarney/react-app-rewired [Next.js]: https://nextjs.org diff --git a/plugins/postcss-custom-properties-import-export/README.md b/plugins/postcss-custom-properties-import-export/README.md index ab48d6d96..5d2b1f898 100644 --- a/plugins/postcss-custom-properties-import-export/README.md +++ b/plugins/postcss-custom-properties-import-export/README.md @@ -1,207 +1,144 @@ -# PostCSS Custom Properties [PostCSS][postcss] +# PostCSS Custom Properties Import/Export [PostCSS Logo][PostCSS] -[![NPM Version][npm-img]][npm-url] -[![CSS Standard Status][css-img]][css-url] -[![Build Status][cli-img]][cli-url] -[Discord][discord] +[npm version][npm-url] [Build Status][cli-url] [Discord][discord] -[PostCSS Custom Properties] lets you use Custom Properties in CSS, following -the [CSS Custom Properties] specification. - -[!['Can I use' table](https://caniuse.bitsofco.de/image/css-variables.png)](https://caniuse.com/#feat=css-variables) - -```pcss -:root { - --color: red; -} - -h1 { - color: var(--color); -} - -/* becomes */ - -:root { - --color: red; -} - -h1 { - color: red; - color: var(--color); -} -``` - -**Note:** This plugin only processes variables that are defined in the `:root` selector. +[PostCSS Custom Properties Import/Export] lets you import or export CSS custom properties (`--foo: pink;`) into or out of your CSS. ## Usage -Add [PostCSS Custom Properties] to your project: +Add [PostCSS Custom Properties Import/Export] to your project: ```bash -npm install postcss-custom-properties --save-dev +npm install postcss @csstools/postcss-custom-properties-import-export --save-dev ``` -Use [PostCSS Custom Properties] as a [PostCSS] plugin: +Use it as a [PostCSS] plugin: ```js const postcss = require('postcss'); -const postcssCustomProperties = require('postcss-custom-properties'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); postcss([ - postcssCustomProperties(/* pluginOptions */) + postcssCustomPropertiesImportExport(/* pluginOptions */) ]).process(YOUR_CSS /*, processOptions */); ``` -[PostCSS Custom Properties] runs in all Node environments, with special instructions for: +[PostCSS Custom Properties Import/Export] 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) | -| --- | --- | --- | --- | --- | --- | +- [Node](INSTALL.md#node) +- [PostCSS CLI](INSTALL.md#postcss-cli) +- [PostCSS Load Config](INSTALL.md#postcss-load-config) +- [Webpack](INSTALL.md#webpack) +- [Create React App](INSTALL.md#create-react-app) +- [Next.js](INSTALL.md#nextjs) +- [Gulp](INSTALL.md#gulp) +- [Grunt](INSTALL.md#grunt) ## Options -### preserve +### importFrom -The `preserve` option determines whether Custom Properties and properties using -custom properties should be preserved in their original form. By default, both -of these are preserved. +The `importFrom` option specifies sources where custom properties can be +imported from, which might be CSS, JS, and JSON files, functions, and directly +passed objects. ```js -postcssCustomProperties({ - preserve: false +postcssCustomPropertiesImportExport({ + importFrom: 'path/to/file.css' // => :root { --color: var(rgb(245 20 255)); } }); ``` ```pcss -:root { - --color: red; -} - -h1 { - color: var(--color); +article { + color: var(--color); } /* becomes */ -h1 { - color: red; +article { + color: var(--color); } -``` -### importFrom - -The `importFrom` option specifies sources where Custom Properties can be imported -from, which might be CSS, JS, and JSON files, functions, and directly passed -objects. - -```js -postcssCustomProperties({ - importFrom: 'path/to/file.css' // => :root { --color: red } -}); -``` - -```pcss -h1 { - color: var(--color); -} - -/* becomes */ - -h1 { - color: red; +:root { + --color: var(rgb(245 20 255)); } ``` 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 Properties using the `customProperties` or +will need to namespace custom properties using the `customProperties` or `custom-properties` key. ```js -postcssCustomProperties({ - importFrom: [ - 'path/to/file.css', // :root { --color: red; } - 'and/then/this.js', // module.exports = { customProperties: { '--color': 'red' } } - 'and/then/that.json', // { "custom-properties": { "--color": "red" } } - { - customProperties: { '--color': 'red' } - }, - () => { - const customProperties = { '--color': 'red' }; - - return { customProperties }; - } - ] +postcssCustomPropertiesImportExport({ + importFrom: [ + 'path/to/file.css', + 'and/then/this.js', + 'and/then/that.json', + { + customProperties: { '--color': 'var(rgb(245 20 255))' } + }, + () => { + const customProperties = { '--color': 'var(rgb(245 20 255))' }; + + return { customProperties }; + } + ] }); ``` -See example imports written in [CSS](test/import-properties.css), -[JS](test/import-properties.js), and [JSON](test/import-properties.json). - ### overrideImportFromWithRoot The `overrideImportFromWithRoot` option determines if properties added via `importFrom` are overridden by properties that exist in `:root`. Defaults to `false`. +_override `importFrom` with `root`_ + ```js -postcssCustomProperties({ +postcssCustomPropertiesImportExport({ overrideImportFromWithRoot: true }); ``` ### exportTo -The `exportTo` option specifies destinations where Custom Properties can be exported -to, which might be CSS, JS, and JSON files, functions, and directly passed -objects. +The `exportTo` option specifies destinations where custom properties can be +exported to, which might be CSS, JS, and JSON files, functions, and directly +passed objects. ```js -postcssCustomProperties({ - exportTo: 'path/to/file.css' // :root { --color: red; } +postcssCustomPropertiesImportExport({ + exportTo: 'path/to/file.css' // :root { --color: var(rgb(245 20 255)); } }); ``` 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 Properties using the `customProperties` or +need to namespace custom properties using the `customProperties` or `custom-properties` key. ```js const cachedObject = { customProperties: {} }; -postcssCustomProperties({ - exportTo: [ - 'path/to/file.css', // :root { --color: red; } - 'and/then/this.js', // module.exports = { customProperties: { '--color': 'red' } } - 'and/then/this.mjs', // export const customProperties = { '--color': 'red' } } - 'and/then/that.json', // { "custom-properties": { "--color": "red" } } - 'and/then/that.scss', // $color: red; - cachedObject, - customProperties => { - customProperties // { '--color': 'red' } - } - ] +postcssCustomPropertiesImportExport({ + exportTo: [ + 'path/to/file.css', // :root { --color: var(rgb(245 20 255)); } + 'and/then/this.js', // module.exports = { customProperties: { '--color': 'var(rgb(245 20 255))' } } + 'and/then/this.mjs', // export const customProperties = { '--color': 'var(rgb(245 20 255))' } } + 'and/then/that.json', // { "custom-properties": { "--color": "var(rgb(245 20 255))" } } + cachedObject, + (customProperties) => { + customProperties // { '--color': 'var(rgb(245 20 255))' } + } + ] }); ``` -See example exports written to [CSS](test/export-properties.css), -[JS](test/export-properties.js), [MJS](test/export-properties.mjs), -[JSON](test/export-properties.json) and [SCSS](test/export-properties.scss). - -### disableDeprecationNotice - -Silence the deprecation notice that is printed to the console when using `importFrom` or `exportTo`. - -> "importFrom" and "exportTo" will be removed in a future version of postcss-custom-properties. -> Check the discussion on github for more details. https://github.com/csstools/postcss-plugins/discussions/192 - -[cli-img]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml/badge.svg [cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test -[css-img]: https://cssdb.org/images/badges/custom-properties.svg -[css-url]: https://cssdb.org/#custom-properties + [discord]: https://discord.gg/bUadyRwkJS -[npm-img]: https://img.shields.io/npm/v/postcss-custom-properties.svg -[npm-url]: https://www.npmjs.com/package/postcss-custom-properties +[npm-url]: https://www.npmjs.com/package/@csstools/postcss-custom-properties-import-export -[CSS Custom Properties]: https://www.w3.org/TR/css-variables-1/ [PostCSS]: https://github.com/postcss/postcss -[PostCSS Custom Properties]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties +[PostCSS Custom Properties Import/Export]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties-import-export diff --git a/plugins/postcss-custom-properties-import-export/docs/README.md b/plugins/postcss-custom-properties-import-export/docs/README.md new file mode 100644 index 000000000..b2f26dbba --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/docs/README.md @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + +
+ +[] lets you import or export CSS custom properties (`--foo: pink;`) into or out of your CSS. + + + + + +## Options + +### importFrom + +The `importFrom` option specifies sources where custom properties can be +imported from, which might be CSS, JS, and JSON files, functions, and directly +passed objects. + +```js +({ + importFrom: 'path/to/file.css' // => :root { --color: var(rgb(245 20 255)); } +}); +``` + +```pcss +article { + color: var(--color); +} + +/* becomes */ + +article { + color: var(--color); +} + +:root { + --color: var(rgb(245 20 255)); +} +``` + +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 properties using the `customProperties` or +`custom-properties` key. + +```js +({ + importFrom: [ + 'path/to/file.css', + 'and/then/this.js', + 'and/then/that.json', + { + customProperties: { '--color': 'var(rgb(245 20 255))' } + }, + () => { + const customProperties = { '--color': 'var(rgb(245 20 255))' }; + + return { customProperties }; + } + ] +}); +``` + +### overrideImportFromWithRoot + +The `overrideImportFromWithRoot` option determines if properties added via `importFrom` are overridden by properties that exist in `:root`. +Defaults to `false`. + +_override `importFrom` with `root`_ + +```js +({ + overrideImportFromWithRoot: true +}); +``` + +### exportTo + +The `exportTo` option specifies destinations where custom properties can be +exported to, which might be CSS, JS, and JSON files, functions, and directly +passed objects. + +```js +({ + exportTo: 'path/to/file.css' // :root { --color: var(rgb(245 20 255)); } +}); +``` + +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 properties using the `customProperties` or +`custom-properties` key. + +```js +const cachedObject = { customProperties: {} }; + +({ + exportTo: [ + 'path/to/file.css', // :root { --color: var(rgb(245 20 255)); } + 'and/then/this.js', // module.exports = { customProperties: { '--color': 'var(rgb(245 20 255))' } } + 'and/then/this.mjs', // export const customProperties = { '--color': 'var(rgb(245 20 255))' } } + 'and/then/that.json', // { "custom-properties": { "--color": "var(rgb(245 20 255))" } } + cachedObject, + (customProperties) => { + customProperties // { '--color': 'var(rgb(245 20 255))' } + } + ] +}); +``` + + diff --git a/plugins/postcss-custom-properties-import-export/package.json b/plugins/postcss-custom-properties-import-export/package.json index 575e9465e..59f149cab 100644 --- a/plugins/postcss-custom-properties-import-export/package.json +++ b/plugins/postcss-custom-properties-import-export/package.json @@ -1,10 +1,24 @@ { "name": "@csstools/postcss-custom-properties-import-export", - "description": "Use Custom Properties Queries in CSS", - "version": "12.1.10", - "author": "Jonathan Neal ", + "description": "Import/Export Custom Properties in CSS", + "version": "1.0.0", "contributors": [ - "Maxime Thirouin" + { + "name": "Antonio Laguna", + "email": "antonio@laguna.es", + "url": "https://antonio.laguna.es" + }, + { + "name": "Romain Menke", + "email": "romainmenke@gmail.com" + }, + { + "name": "Jonathan Neal", + "email": "jonathantneal@hotmail.com" + }, + { + "name": "Maxime Thirouin" + } ], "license": "MIT", "funding": { @@ -12,7 +26,7 @@ "url": "https://opencollective.com/csstools" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "main": "dist/index.cjs", "module": "dist/index.mjs", @@ -31,19 +45,17 @@ "dist", "index.d.ts" ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" }, "devDependencies": { + "postcss-custom-properties": "^13.0.0", "postcss-import": "^15.0.0" }, "scripts": { - "build": "rollup -c ../../rollup/default.js", + "build": "rollup -c ../../rollup/default.mjs", "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", - "docs": "node ../../.github/bin/generate-docs/install.mjs", + "docs": "node ../../.github/bin/generate-docs/install.mjs && node ../../.github/bin/generate-docs/readme.mjs", "lint": "npm run lint:eslint && npm run lint:package-json", "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", @@ -52,29 +64,26 @@ "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs", "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs" }, - "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties#readme", + "homepage": "https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties-import-export#readme", "repository": { "type": "git", "url": "https://github.com/csstools/postcss-plugins.git", - "directory": "plugins/postcss-custom-properties" + "directory": "plugins/postcss-custom-properties-import-export" }, "bugs": "https://github.com/csstools/postcss-plugins/issues", "keywords": [ "css", - "csswg", "custom", "declarations", "postcss", "postcss-plugin", "properties", - "specification", "variables", - "vars", - "w3c" + "vars" ], "csstools": { - "exportName": "postcssCustomProperties", - "humanReadableName": "PostCSS Custom Properties" + "exportName": "postcssCustomPropertiesImportExport", + "humanReadableName": "PostCSS Custom Properties Import/Export" }, "volta": { "extends": "../../package.json" diff --git a/plugins/postcss-custom-properties-import-export/src/index.ts b/plugins/postcss-custom-properties-import-export/src/index.ts index 22b2f1187..90977e8f9 100644 --- a/plugins/postcss-custom-properties-import-export/src/index.ts +++ b/plugins/postcss-custom-properties-import-export/src/index.ts @@ -1,18 +1,10 @@ -import type { PluginCreator } from 'postcss'; -import type valuesParser from 'postcss-value-parser'; - -import getCustomPropertiesFromRoot from './lib/get-custom-properties-from-root'; import getCustomPropertiesFromImports from './lib/get-custom-properties-from-imports'; -import transformProperties from './lib/transform-properties'; +import getCustomPropertiesFromRoot from './lib/get-custom-properties-from-root'; import writeCustomPropertiesToExports from './lib/write-custom-properties-to-exports'; import type { ImportOptions, ExportOptions } from './lib/options'; +import type { PluginCreator } from 'postcss'; export interface PluginOptions { - /** Do not emit warnings about "importFrom" and "exportTo" deprecations */ - disableDeprecationNotice?: boolean; - /** Determines whether Custom Properties and properties using custom properties should be preserved in their original form. */ - preserve?: boolean - /** Specifies sources where Custom Properties can be imported from, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ importFrom?: ImportOptions | Array @@ -24,10 +16,7 @@ export interface PluginOptions { } const creator: PluginCreator = (opts?: PluginOptions) => { - // whether to preserve custom selectors and rules using them - const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : true; const overrideImportFromWithRoot = 'overrideImportFromWithRoot' in Object(opts) ? Boolean(opts.overrideImportFromWithRoot) : false; - const disableDeprecationNotice = 'disableDeprecationNotice' in Object(opts) ? Boolean(opts.disableDeprecationNotice) : false; // sources to import custom selectors from let importFrom: Array = []; @@ -48,56 +37,79 @@ const creator: PluginCreator = (opts?: PluginOptions) => { // promise any custom selectors are imported const customPropertiesPromise = getCustomPropertiesFromImports(importFrom); - // whether to return synchronous function if no asynchronous operations are requested - const canReturnSyncFunction = importFrom.length === 0 && exportTo.length === 0; - return { postcssPlugin: 'postcss-custom-properties', prepare () { - let customProperties: Map = new Map(); - - if (canReturnSyncFunction) { - return { - Once: (root) => { - customProperties = getCustomPropertiesFromRoot(root, { preserve }); - }, - Declaration: (decl) => { - transformProperties(decl, customProperties, { preserve }); - }, - OnceExit: () => { - customProperties.clear(); - }, - }; - } else { - return { - Once: async root => { - const importedCustomerProperties = (await customPropertiesPromise).entries(); - const rootCustomProperties = getCustomPropertiesFromRoot(root, { preserve: preserve }).entries(); - - if (overrideImportFromWithRoot) { - for (const [name, value] of [...importedCustomerProperties, ...rootCustomProperties]) { - customProperties.set(name, value); - } - } else { - for (const [name, value] of [...rootCustomProperties, ...importedCustomerProperties]) { - customProperties.set(name, value); - } + const customPropertiesForInsertion: Map = new Map(); + const customPropertiesForExport: Map = new Map(); + + return { + Once: async (root, { result, postcss }) => { + const importedCustomerProperties = (await customPropertiesPromise).entries(); + const rootCustomProperties = getCustomPropertiesFromRoot(root).entries(); + + for (const [name, value] of importedCustomerProperties) { + customPropertiesForInsertion.set(name, value); + } + + if (overrideImportFromWithRoot) { + for (const [name, value] of [...importedCustomerProperties, ...rootCustomProperties]) { + customPropertiesForExport.set(name, value); } + } else { + for (const [name, value] of [...rootCustomProperties, ...importedCustomerProperties]) { + customPropertiesForExport.set(name, value); + } + } + + await writeCustomPropertiesToExports(customPropertiesForExport, exportTo); + + if (customPropertiesForInsertion.size > 0) { + const propertyNames = Array.from(customPropertiesForInsertion.keys()); + // Inserting in reverse order results in the correct order. + propertyNames.reverse(); - await writeCustomPropertiesToExports(customProperties, exportTo); - }, - Declaration: (decl) => { - transformProperties(decl, customProperties, { preserve }); - }, - OnceExit: (root, { result }) => { - if (!disableDeprecationNotice && (importFrom.length > 0 || exportTo.length > 0)) { - root.warn(result, '"importFrom" and "exportTo" will be removed in a future version of postcss-custom-properties.\nWe are looking for insights and anecdotes on how these features are used so that we can design the best alternative.\nPlease let us know if our proposal will work for you.\nVisit the discussion on github for more details. https://github.com/csstools/postcss-plugins/discussions/192'); + let operator = 'prepend'; + if (!overrideImportFromWithRoot) { + operator = 'append'; } - customProperties.clear(); - }, - }; - } + const rootRule = postcss.rule({ + selector: ':root', + source: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + input: { + from: result.opts.from, + }, + start: { offset: 0, line: 1, column: 1 }, + end: { offset: 0, line: 1, column: 1 }, + }, + }); + + root[operator](rootRule); + + propertyNames.forEach((propertyName) => { + const decl = postcss.decl({ + prop: propertyName, + value: customPropertiesForInsertion.get(propertyName), + }); + + decl.source = { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + input: { + from: result.opts.from, + }, + start: { offset: 0, line: 1, column: 1 }, + end: { offset: 0, line: 1, column: 1 }, + }; + + rootRule.append(decl); + }); + } + }, + }; }, }; }; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts index 1296201a0..9e9cab8a5 100644 --- a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts +++ b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts @@ -2,7 +2,6 @@ import getCustomPropertiesFromRoot from './get-custom-properties-from-root'; import path from 'path'; import { pathToFileURL } from 'url'; import type { ImportCustomProperties, ImportOptions } from './options'; -import valuesParser from 'postcss-value-parser'; import { parse } from 'postcss'; import { promises as fsp } from 'fs'; @@ -10,27 +9,27 @@ import { promises as fsp } from 'fs'; /* ========================================================================== */ async function getCustomPropertiesFromCSSFile(from) { - const css = await fsp.readFile(from); + const css = await fsp.readFile(pathToFileURL(from)); const root = parse(css, { from : from.toString() }); - return getCustomPropertiesFromRoot(root, { preserve: true }); + return getCustomPropertiesFromRoot(root); } /* Get Custom Properties from Object /* ========================================================================== */ -function getCustomPropertiesFromObject(object: ImportCustomProperties): Map { - const out: Map = new Map(); +function getCustomPropertiesFromObject(object: ImportCustomProperties): Map { + const out: Map = new Map(); if ('customProperties' in object) { for (const [name, value] of Object.entries(object.customProperties)) { - out.set(name, valuesParser(value.toString())); + out.set(name, value.toString()); } } if ('custom-properties' in object) { for (const [name, value] of Object.entries(object['custom-properties'])) { - out.set(name, valuesParser(value.toString())); + out.set(name, value.toString()); } } @@ -40,8 +39,8 @@ function getCustomPropertiesFromObject(object: ImportCustomProperties): Map> { - const object = await readJSON(from); +async function getCustomPropertiesFromJSONFile(from): Promise> { + const object = await readJSON(pathToFileURL(from)); return getCustomPropertiesFromObject(object); } @@ -49,15 +48,8 @@ async function getCustomPropertiesFromJSONFile(from): Promise> { - let object; - - try { - object = await import(from); - } catch (_) { - // windows support - object = await import(pathToFileURL(from).href); - } +async function getCustomPropertiesFromJSFile(from): Promise> { + const object = await import(pathToFileURL(from).href); if ('default' in object) { return getCustomPropertiesFromObject(object.default); @@ -69,7 +61,7 @@ async function getCustomPropertiesFromJSFile(from): Promise): Promise> { +export default async function getCustomPropertiesFromImports(sources: Array): Promise> { const sourceData = (await Promise.all(sources.map(async (source) => { if (source instanceof Promise) { source = await source; @@ -117,7 +109,7 @@ export default async function getCustomPropertiesFromImports(sources: Array> = await Promise.all(sourceData.map(async (partialData) => { + const data: Array> = await Promise.all(sourceData.map(async (partialData) => { if (('type' in partialData) && ('from' in partialData)) { if (partialData.type === 'css' || partialData.type === 'pcss') { return await getCustomPropertiesFromCSSFile(partialData.from); @@ -142,7 +134,7 @@ export default async function getCustomPropertiesFromImports(sources: Array = new Map(); + const out: Map = new Map(); data.forEach((partialData) => { for (const [name, value] of partialData.entries()) { out.set(name, value); diff --git a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts index e177478fa..4de0dafe9 100644 --- a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts +++ b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-root.ts @@ -1,12 +1,9 @@ -import valuesParser from 'postcss-value-parser'; -import { isBlockIgnored } from './is-ignored'; - // return custom selectors from the css root, conditionally removing them -export default function getCustomPropertiesFromRoot(root, opts): Map { +export default function getCustomPropertiesFromRoot(root): Map { // initialize custom selectors - const customPropertiesFromHtmlElement: Map = new Map(); - const customPropertiesFromRootPseudo: Map = new Map(); - const out: Map = new Map(); + const customPropertiesFromHtmlElement: Map = new Map(); + const customPropertiesFromRootPseudo: Map = new Map(); + const out: Map = new Map(); // for each html or :root rule root.nodes.slice().forEach(rule => { @@ -19,23 +16,13 @@ export default function getCustomPropertiesFromRoot(root, opts): Map { - if (decl.variable && !isBlockIgnored(decl)) { + if (decl.variable) { const { prop } = decl; // write the parsed value to the custom property - customPropertiesObject.set(prop, valuesParser(decl.value)); - - // conditionally remove the custom property declaration - if (!opts.preserve) { - decl.remove(); - } + customPropertiesObject.set(prop, decl.value); } }); - - // conditionally remove the empty html or :root rule - if (!opts.preserve && isEmptyParent(rule) && !isBlockIgnored(rule)) { - rule.remove(); - } } }); @@ -58,6 +45,3 @@ const rootSelectorRegExp = /^:root$/i; // whether the node is an html or :root rule const isHtmlRule = node => node.type === 'rule' && node.selector.split(',').some(item => htmlSelectorRegExp.test(item)) && Object(node.nodes).length; const isRootRule = node => node.type === 'rule' && node.selector.split(',').some(item => rootSelectorRegExp.test(item)) && Object(node.nodes).length; - -// whether the node is a parent without children -const isEmptyParent = node => Object(node.nodes).length === 0; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts b/plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts deleted file mode 100644 index e1c5ccc5c..000000000 --- a/plugins/postcss-custom-properties-import-export/src/lib/is-ignored.ts +++ /dev/null @@ -1,20 +0,0 @@ -function isBlockIgnored(ruleOrDeclaration) { - const rule = ruleOrDeclaration.selector ? - ruleOrDeclaration : ruleOrDeclaration.parent; - - return /(!\s*)?postcss-custom-properties:\s*off\b/i.test(rule.toString()); -} - -function isRuleIgnored(rule) { - const previous = rule.prev(); - - return Boolean(isBlockIgnored(rule) || - previous && - previous.type === 'comment' && - /(!\s*)?postcss-custom-properties:\s*ignore\s+next\b/i.test(previous.text)); -} - -export { - isBlockIgnored, - isRuleIgnored, -}; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts b/plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts deleted file mode 100644 index da2a8147a..000000000 --- a/plugins/postcss-custom-properties-import-export/src/lib/transform-properties.ts +++ /dev/null @@ -1,86 +0,0 @@ -import valuesParser from 'postcss-value-parser'; -import transformValueAST from './transform-value-ast'; -import { isRuleIgnored } from './is-ignored'; -import { Declaration } from 'postcss'; - -// transform custom pseudo selectors with custom selectors -export default (decl, customProperties, opts) => { - if (isTransformableDecl(decl) && !isRuleIgnored(decl)) { - const originalValue = decl.value; - const valueAST = valuesParser(originalValue); - let value = transformValueAST(valueAST, customProperties); - - // protect against circular references - const valueSet = new Set(); - - while (value.includes('--') && value.includes('var(') && !valueSet.has(value)) { - valueSet.add(value); - const parsedValueAST = valuesParser(value); - value = transformValueAST(parsedValueAST, customProperties); - } - - // conditionally transform values that have changed - if (value !== originalValue) { - if (parentHasExactFallback(decl, value)) { - if (!opts.preserve) { - decl.remove(); - } - - return; - } - - if (opts.preserve) { - const beforeDecl = decl.cloneBefore({ value }); - - if (hasTrailingComment(beforeDecl)) { - beforeDecl.raws.value.value = beforeDecl.value.replace(trailingCommentRegExp, '$1'); - beforeDecl.raws.value.raw = beforeDecl.raws.value.value + beforeDecl.raws.value.raw.replace(trailingCommentRegExp, '$2'); - } - } else { - decl.value = value; - - if (hasTrailingComment(decl)) { - decl.raws.value.value = decl.value.replace(trailingCommentRegExp, '$1'); - decl.raws.value.raw = decl.raws.value.value + decl.raws.value.raw.replace(trailingCommentRegExp, '$2'); - } - } - } - } -}; - -// match custom properties - -// whether the declaration should be potentially transformed -const isTransformableDecl = decl => !decl.variable && decl.value.includes('--') && decl.value.includes('var('); - -// whether the declaration has a trailing comment -const hasTrailingComment = decl => 'value' in Object(Object(decl.raws).value) && 'raw' in decl.raws.value && trailingCommentRegExp.test(decl.raws.value.raw); -const trailingCommentRegExp = /^([\W\w]+)(\s*\/\*[\W\w]+?\*\/)$/; - -function parentHasExactFallback(decl: Declaration, value: string): boolean { - if (!decl || !decl.parent) { - return false; - } - - let hasFallback = false; - const declIndex = decl.parent.index(decl); - decl.parent.each((sibling, index) => { - if (sibling === decl) { - return false; - } - - if (index >= declIndex) { - return false; - } - - if (sibling.type !== 'decl') { - return; - } - - if (sibling.prop.toLowerCase() === decl.prop.toLowerCase() && sibling.value === value) { - hasFallback = true; - } - }); - - return hasFallback; -} diff --git a/plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts b/plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts deleted file mode 100644 index 10d42b10b..000000000 --- a/plugins/postcss-custom-properties-import-export/src/lib/transform-value-ast.ts +++ /dev/null @@ -1,50 +0,0 @@ -export default function transformValueAST(root, customProperties) { - if (root.nodes && root.nodes.length) { - root.nodes.slice().forEach((child) => { - if (isVarFunction(child)) { - const [propertyNode, ...fallbacks] = child.nodes.filter((node) => node.type !== 'div'); - const { value: name } = propertyNode; - const index = root.nodes.indexOf(child); - - if (customProperties.has(name)) { - // Direct match of a custom property to a parsed value - const nodes = customProperties.get(name).nodes; - - // Re-transform nested properties without given one to avoid circular from keeping this forever - reTransformValueAST({ nodes }, customProperties, name); - - if (index > -1) { - root.nodes.splice(index, 1, ...nodes); - } - } else if (fallbacks.length) { - // No match, but fallback available - if (index > -1) { - root.nodes.splice(index, 1, ...child.nodes.slice(child.nodes.indexOf(fallbacks[0]))); - } - - transformValueAST(root, customProperties); - } - } else { - // Transform child nodes of current child - transformValueAST(child, customProperties); - } - }); - } - - return root.toString(); -} - -// reTransform the current ast without a custom property (to prevent recursion) -function reTransformValueAST(root, customProperties, withoutProperty) { - const nextCustomProperties = new Map(customProperties); - - nextCustomProperties.delete(withoutProperty); - - return transformValueAST(root, nextCustomProperties); -} - -// match var() functions -const varRegExp = /^var$/i; - -// whether the node is a var() function -const isVarFunction = node => node.type === 'function' && varRegExp.test(node.value) && Object(node.nodes).length > 0; diff --git a/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts b/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts index bbd0b1cb6..61a9948d5 100644 --- a/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts +++ b/plugins/postcss-custom-properties-import-export/src/lib/write-custom-properties-to-exports.ts @@ -1,5 +1,3 @@ -import type valuesParser from 'postcss-value-parser'; - import { promises as fsp } from 'fs'; import path from 'path'; import type { ExportOptions } from './options'; @@ -145,7 +143,7 @@ async function writePropertiesToFile(to: string, type: string, customProperties: /* Helper utilities /* ========================================================================== */ -function defaultCustomPropertiesToJSONObject(customProperties: Map): Record { +function defaultCustomPropertiesToJSONObject(customProperties: Map): Record { const out = {}; for (const [name, value] of customProperties.entries()) { out[name] = value.toString(); diff --git a/plugins/postcss-custom-properties-import-export/test/_import.mjs b/plugins/postcss-custom-properties-import-export/test/_import.mjs index 98f876766..2f8dedcfd 100644 --- a/plugins/postcss-custom-properties-import-export/test/_import.mjs +++ b/plugins/postcss-custom-properties-import-export/test/_import.mjs @@ -1,5 +1,5 @@ import assert from 'assert'; -import plugin from 'postcss-custom-properties'; +import plugin from '@csstools/postcss-custom-properties-import-export'; import postcss from 'postcss'; import fs from 'fs'; diff --git a/plugins/postcss-custom-properties-import-export/test/_require.cjs b/plugins/postcss-custom-properties-import-export/test/_require.cjs index a9a3a0897..91bcaac37 100644 --- a/plugins/postcss-custom-properties-import-export/test/_require.cjs +++ b/plugins/postcss-custom-properties-import-export/test/_require.cjs @@ -1,5 +1,5 @@ const assert = require('assert'); -const plugin = require('postcss-custom-properties'); +const plugin = require('@csstools/postcss-custom-properties-import-export'); const postcss = require('postcss'); const fs = require('fs'); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.expect.css index 849754a3d..8715e58b1 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.expect.css @@ -20,7 +20,6 @@ html { --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); --✅-size: 2em; - color: rgb(255, 0, 0); color: var(--color); } @@ -32,7 +31,6 @@ html { .ignore-line { /* postcss-custom-properties: ignore next */ color: var(--color); - background-color: blue; background-color: var(--color-2, blue); } @@ -45,42 +43,34 @@ html { .test { --skip: gray; - color: rgb(255, 0, 0); color: var(--override, var(--color)); } .test--color_spacing { - box-shadow: inset 0 -3px 0 rgb(255, 0, 0); box-shadow: inset 0 -3px 0 var(--color); } .test--preserve_whitespaces { - margin: 0 10px 20px 30px; margin: var(--margin); } .test--complex_values { - box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); box-shadow: var(--shadow); } .test--comma_separated_values { - font-family: "Open Sans", sans-serif; font-family: var(--font-family); } .test--fallback { - color: blue; color: var(--color-2, blue); } .test--color_w_var { - color: rgb(255, 0, 0); color: var(--ref-color); } .test--color_w_vars { - color: hsl(0, 100%, 50%); color: var(--color-hsl); } @@ -93,22 +83,18 @@ html { } .test--nested-fallback { - z-index: 1; z-index: var(--xxx, var(--yyy, 1)); } .text--calc { - width: calc((100% - 1px) + 10px); width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); } .test--linear-gradient { - background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); } .test--loose-formatting { - color: rgb(255, 0, 0)/*rtl:red*/; color: var( --color, blue @@ -116,33 +102,28 @@ html { } .test--combined-selector { - color: #053; color: var(--theme-color); } .test--variable-with-url { order: 1; - background: url("/my/path"); background: var(--url-1); } .test--variable-with-url { order: 2; - background: url('/my/path'); background: var(--url-2); } .test--variable-with-url { order: 3; - background: url(/my/path); background: var(--url-3); } .test--variable-with-url { order: 4; - background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); background: var(--url-4); } @@ -151,12 +132,10 @@ html { } .test-unicode { - color: 2em; color: var(--✅-size); } .test { - font-family: "Helvetica Neue", Arial, sans-serif; font-family: var(--font, "Helvetica Neue", Arial, sans-serif); } @@ -171,13 +150,11 @@ html { } .does-not-ignore-declarations-that-have-an-exact-override-a { - left: 1rem; left: var(--does-not-exist, 1rem); left: 1rem; } .does-not-ignore-declarations-that-have-an-exact-override-b { - right: 2em; right: var(--✅-size); right: 2em; } diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css new file mode 100644 index 000000000..8715e58b1 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css @@ -0,0 +1,160 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css new file mode 100644 index 000000000..29a8a4cf4 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --z-index: 10; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css new file mode 100644 index 000000000..42909d79c --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css @@ -0,0 +1,168 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css index 849754a3d..8715e58b1 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css @@ -20,7 +20,6 @@ html { --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); --✅-size: 2em; - color: rgb(255, 0, 0); color: var(--color); } @@ -32,7 +31,6 @@ html { .ignore-line { /* postcss-custom-properties: ignore next */ color: var(--color); - background-color: blue; background-color: var(--color-2, blue); } @@ -45,42 +43,34 @@ html { .test { --skip: gray; - color: rgb(255, 0, 0); color: var(--override, var(--color)); } .test--color_spacing { - box-shadow: inset 0 -3px 0 rgb(255, 0, 0); box-shadow: inset 0 -3px 0 var(--color); } .test--preserve_whitespaces { - margin: 0 10px 20px 30px; margin: var(--margin); } .test--complex_values { - box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); box-shadow: var(--shadow); } .test--comma_separated_values { - font-family: "Open Sans", sans-serif; font-family: var(--font-family); } .test--fallback { - color: blue; color: var(--color-2, blue); } .test--color_w_var { - color: rgb(255, 0, 0); color: var(--ref-color); } .test--color_w_vars { - color: hsl(0, 100%, 50%); color: var(--color-hsl); } @@ -93,22 +83,18 @@ html { } .test--nested-fallback { - z-index: 1; z-index: var(--xxx, var(--yyy, 1)); } .text--calc { - width: calc((100% - 1px) + 10px); width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); } .test--linear-gradient { - background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); } .test--loose-formatting { - color: rgb(255, 0, 0)/*rtl:red*/; color: var( --color, blue @@ -116,33 +102,28 @@ html { } .test--combined-selector { - color: #053; color: var(--theme-color); } .test--variable-with-url { order: 1; - background: url("/my/path"); background: var(--url-1); } .test--variable-with-url { order: 2; - background: url('/my/path'); background: var(--url-2); } .test--variable-with-url { order: 3; - background: url(/my/path); background: var(--url-3); } .test--variable-with-url { order: 4; - background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); background: var(--url-4); } @@ -151,12 +132,10 @@ html { } .test-unicode { - color: 2em; color: var(--✅-size); } .test { - font-family: "Helvetica Neue", Arial, sans-serif; font-family: var(--font, "Helvetica Neue", Arial, sans-serif); } @@ -171,13 +150,11 @@ html { } .does-not-ignore-declarations-that-have-an-exact-override-a { - left: 1rem; left: var(--does-not-exist, 1rem); left: 1rem; } .does-not-ignore-declarations-that-have-an-exact-override-b { - right: 2em; right: var(--✅-size); right: 2em; } diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css new file mode 100644 index 000000000..29a8a4cf4 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --z-index: 10; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css new file mode 100644 index 000000000..2aa3d2ae5 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css @@ -0,0 +1,167 @@ +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: var(--color-2, blue); +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: var(--margin); +} + +.test--complex_values { + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: var(--font-family); +} + +.test--fallback { + color: var(--color-2, blue); +} + +.test--color_w_var { + color: var(--ref-color); +} + +.test--color_w_vars { + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} + +.test { + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); + right: 2em; +} + +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css index 384a4d6f1..0c591d8e8 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css @@ -1,11 +1,37 @@ +html { + --ref-color: skip; +} + :root { - color: rgb(0, 0, 0); + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; } .ignore-line { /* postcss-custom-properties: ignore next */ color: var(--color); - background-color: yellow; + background-color: var(--color-2, blue); } .ignore-block { @@ -17,35 +43,35 @@ .test { --skip: gray; - color: rgb(0, 0, 0); + color: var(--override, var(--color)); } .test--color_spacing { - box-shadow: inset 0 -3px 0 rgb(0, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); } .test--preserve_whitespaces { - margin: 0 10px 20px 30px; + margin: var(--margin); } .test--complex_values { - box-shadow: 0 6px 14px 0 color(rgb(0,0,0) a(.15)); + box-shadow: var(--shadow); } .test--comma_separated_values { - font-family: "Open Sans", sans-serif; + font-family: var(--font-family); } .test--fallback { - color: yellow; + color: var(--color-2, blue); } .test--color_w_var { - color: rgb(0, 0, 0); + color: var(--ref-color); } .test--color_w_vars { - color: hsl(0, 100%, 50%); + color: var(--color-hsl); } .test--circular_var { @@ -53,49 +79,52 @@ } .test--z-index { - z-index: 10; + z-index: var(--z-index); } .test--nested-fallback { - z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); } .text--calc { - width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); } .test--linear-gradient { - background-image: linear-gradient(to right, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); } .test--loose-formatting { - color: rgb(0, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; } .test--combined-selector { - color: #053; + color: var(--theme-color); } .test--variable-with-url { order: 1; - background: url("/my/path"); + background: var(--url-1); } .test--variable-with-url { order: 2; - background: url('/my/path'); + background: var(--url-2); } .test--variable-with-url { order: 3; - background: url(/my/path); + background: var(--url-3); } .test--variable-with-url { order: 4; - background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); } .no-prototype-collisions { @@ -103,27 +132,38 @@ } .test-unicode { - color: 2em; + color: var(--✅-size); } .test { - font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); } .ignores-declarations-that-have-an-exact-fallback-a { left: 1rem; + left: var(--does-not-exist, 1rem); } .ignores-declarations-that-have-an-exact-fallback-b { right: 2em; + right: var(--✅-size); } .does-not-ignore-declarations-that-have-an-exact-override-a { - left: 1rem; + left: var(--does-not-exist, 1rem); left: 1rem; } .does-not-ignore-declarations-that-have-an-exact-override-b { + right: var(--✅-size); right: 2em; - right: 2em; +} + +:root { + --z-index: 10; + --shadow-color: rgb(0,0,0); + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(0, 0, 0); } diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css index a784a4b6c..ffa824579 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css @@ -1,11 +1,46 @@ :root { - color: rgb(255, 0, 0); + --z-index: 10; + --shadow-color: rgb(0,0,0); + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(0, 0, 0); +} + +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; } .ignore-line { /* postcss-custom-properties: ignore next */ color: var(--color); - background-color: yellow; + background-color: var(--color-2, blue); } .ignore-block { @@ -17,35 +52,35 @@ .test { --skip: gray; - color: rgb(255, 0, 0); + color: var(--override, var(--color)); } .test--color_spacing { - box-shadow: inset 0 -3px 0 rgb(255, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); } .test--preserve_whitespaces { - margin: 0 10px 20px 30px; + margin: var(--margin); } .test--complex_values { - box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); + box-shadow: var(--shadow); } .test--comma_separated_values { - font-family: "Open Sans", sans-serif; + font-family: var(--font-family); } .test--fallback { - color: yellow; + color: var(--color-2, blue); } .test--color_w_var { - color: rgb(255, 0, 0); + color: var(--ref-color); } .test--color_w_vars { - color: hsl(0, 100%, 50%); + color: var(--color-hsl); } .test--circular_var { @@ -53,49 +88,52 @@ } .test--z-index { - z-index: 10; + z-index: var(--z-index); } .test--nested-fallback { - z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); } .text--calc { - width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); } .test--linear-gradient { - background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); } .test--loose-formatting { - color: rgb(255, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; } .test--combined-selector { - color: #053; + color: var(--theme-color); } .test--variable-with-url { order: 1; - background: url("/my/path"); + background: var(--url-1); } .test--variable-with-url { order: 2; - background: url('/my/path'); + background: var(--url-2); } .test--variable-with-url { order: 3; - background: url(/my/path); + background: var(--url-3); } .test--variable-with-url { order: 4; - background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); } .no-prototype-collisions { @@ -103,27 +141,29 @@ } .test-unicode { - color: 2em; + color: var(--✅-size); } .test { - font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); } .ignores-declarations-that-have-an-exact-fallback-a { left: 1rem; + left: var(--does-not-exist, 1rem); } .ignores-declarations-that-have-an-exact-fallback-b { right: 2em; + right: var(--✅-size); } .does-not-ignore-declarations-that-have-an-exact-override-a { - left: 1rem; + left: var(--does-not-exist, 1rem); left: 1rem; } .does-not-ignore-declarations-that-have-an-exact-override-b { - right: 2em; + right: var(--✅-size); right: 2em; } diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css index be1b4f513..42909d79c 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css @@ -20,7 +20,6 @@ html { --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); --✅-size: 2em; - color: rgb(255, 0, 0); color: var(--color); } @@ -32,7 +31,6 @@ html { .ignore-line { /* postcss-custom-properties: ignore next */ color: var(--color); - background-color: yellow; background-color: var(--color-2, blue); } @@ -45,42 +43,34 @@ html { .test { --skip: gray; - color: rgb(255, 0, 0); color: var(--override, var(--color)); } .test--color_spacing { - box-shadow: inset 0 -3px 0 rgb(255, 0, 0); box-shadow: inset 0 -3px 0 var(--color); } .test--preserve_whitespaces { - margin: 0 10px 20px 30px; margin: var(--margin); } .test--complex_values { - box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); box-shadow: var(--shadow); } .test--comma_separated_values { - font-family: "Open Sans", sans-serif; font-family: var(--font-family); } .test--fallback { - color: yellow; color: var(--color-2, blue); } .test--color_w_var { - color: rgb(255, 0, 0); color: var(--ref-color); } .test--color_w_vars { - color: hsl(0, 100%, 50%); color: var(--color-hsl); } @@ -89,27 +79,22 @@ html { } .test--z-index { - z-index: 10; z-index: var(--z-index); } .test--nested-fallback { - z-index: 1; z-index: var(--xxx, var(--yyy, 1)); } .text--calc { - width: calc((100% - 1px) + 10px); width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); } .test--linear-gradient { - background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); } .test--loose-formatting { - color: rgb(255, 0, 0)/*rtl:red*/; color: var( --color, blue @@ -117,33 +102,28 @@ html { } .test--combined-selector { - color: #053; color: var(--theme-color); } .test--variable-with-url { order: 1; - background: url("/my/path"); background: var(--url-1); } .test--variable-with-url { order: 2; - background: url('/my/path'); background: var(--url-2); } .test--variable-with-url { order: 3; - background: url(/my/path); background: var(--url-3); } .test--variable-with-url { order: 4; - background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); background: var(--url-4); } @@ -152,12 +132,10 @@ html { } .test-unicode { - color: 2em; color: var(--✅-size); } .test { - font-family: "Helvetica Neue", Arial, sans-serif; font-family: var(--font, "Helvetica Neue", Arial, sans-serif); } @@ -172,13 +150,19 @@ html { } .does-not-ignore-declarations-that-have-an-exact-override-a { - left: 1rem; left: var(--does-not-exist, 1rem); left: 1rem; } .does-not-ignore-declarations-that-have-an-exact-override-b { - right: 2em; right: var(--✅-size); right: 2em; } + +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css index fcd1ef3ac..8715e58b1 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css @@ -1,11 +1,37 @@ +html { + --ref-color: skip; +} + :root { - color: rgb(255, 0, 0); + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; } .ignore-line { /* postcss-custom-properties: ignore next */ color: var(--color); - background-color: blue; + background-color: var(--color-2, blue); } .ignore-block { @@ -17,35 +43,35 @@ .test { --skip: gray; - color: rgb(255, 0, 0); + color: var(--override, var(--color)); } .test--color_spacing { - box-shadow: inset 0 -3px 0 rgb(255, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); } .test--preserve_whitespaces { - margin: 0 10px 20px 30px; + margin: var(--margin); } .test--complex_values { - box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); + box-shadow: var(--shadow); } .test--comma_separated_values { - font-family: "Open Sans", sans-serif; + font-family: var(--font-family); } .test--fallback { - color: blue; + color: var(--color-2, blue); } .test--color_w_var { - color: rgb(255, 0, 0); + color: var(--ref-color); } .test--color_w_vars { - color: hsl(0, 100%, 50%); + color: var(--color-hsl); } .test--circular_var { @@ -57,45 +83,48 @@ } .test--nested-fallback { - z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); } .text--calc { - width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); } .test--linear-gradient { - background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); } .test--loose-formatting { - color: rgb(255, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; } .test--combined-selector { - color: #053; + color: var(--theme-color); } .test--variable-with-url { order: 1; - background: url("/my/path"); + background: var(--url-1); } .test--variable-with-url { order: 2; - background: url('/my/path'); + background: var(--url-2); } .test--variable-with-url { order: 3; - background: url(/my/path); + background: var(--url-3); } .test--variable-with-url { order: 4; - background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); } .no-prototype-collisions { @@ -103,27 +132,29 @@ } .test-unicode { - color: 2em; + color: var(--✅-size); } .test { - font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); } .ignores-declarations-that-have-an-exact-fallback-a { left: 1rem; + left: var(--does-not-exist, 1rem); } .ignores-declarations-that-have-an-exact-fallback-b { right: 2em; + right: var(--✅-size); } .does-not-ignore-declarations-that-have-an-exact-override-a { - left: 1rem; + left: var(--does-not-exist, 1rem); left: 1rem; } .does-not-ignore-declarations-that-have-an-exact-override-b { - right: 2em; + right: var(--✅-size); right: 2em; } diff --git a/plugins/postcss-custom-properties-import-export/test/examples/.gitkeep b/plugins/postcss-custom-properties-import-export/test/examples/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/postcss-custom-properties-import-export/test/import.expect.css b/plugins/postcss-custom-properties-import-export/test/import.expect.css index c7fcc0ac0..03939d1a2 100644 --- a/plugins/postcss-custom-properties-import-export/test/import.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/import.expect.css @@ -3,6 +3,5 @@ } a { - color: blue; color: var(--color); } diff --git a/plugins/postcss-custom-selectors-import-export/.tape.cjs b/plugins/postcss-custom-selectors-import-export/.tape.cjs index c2ec61cdf..0ab5de880 100644 --- a/plugins/postcss-custom-selectors-import-export/.tape.cjs +++ b/plugins/postcss-custom-selectors-import-export/.tape.cjs @@ -16,6 +16,40 @@ postcssTape(plugin)({ } } }, + 'basic-import:override-import-from-with-root:true': { + message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + options: { + overrideImportFromWithRoot: true, + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }, + exportTo(customProperties) { + if (customProperties[':--text'] !== 'p, b, strong, i, em, quote, cite') { + throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); + } + } + } + }, + 'basic-import:override-import-from-with-root:false': { + message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + options: { + overrideImportFromWithRoot: false, + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }, + exportTo(customProperties) { + if (customProperties[':--text'] !== ':--heading, p') { + throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); + } + } + } + }, 'basic-import:fn': { message: 'supports { importFrom() } usage', options: { diff --git a/plugins/postcss-custom-selectors-import-export/.tape.mjs b/plugins/postcss-custom-selectors-import-export/.tape.mjs index fb88f8fad..4aa2bc59e 100644 --- a/plugins/postcss-custom-selectors-import-export/.tape.mjs +++ b/plugins/postcss-custom-selectors-import-export/.tape.mjs @@ -32,6 +32,40 @@ postcssTape(plugin)({ polyfillPlugin(), ] }, + 'basic-import:override-import-from-with-root:true': { + message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + options: { + overrideImportFromWithRoot: true, + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }, + exportTo(customProperties) { + if (customProperties[':--text'] !== 'p, b, strong, i, em, quote, cite') { + throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); + } + } + } + }, + 'basic-import:override-import-from-with-root:false': { + message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + options: { + overrideImportFromWithRoot: false, + importFrom: { + customSelectors: { + ':--heading': 'h1, h2, h3', + ':--text': ':--heading, p', + } + }, + exportTo(customProperties) { + if (customProperties[':--text'] !== ':--heading, p') { + throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); + } + } + } + }, 'basic-import:fn': { message: 'supports { importFrom() } usage', options: { diff --git a/plugins/postcss-custom-selectors-import-export/README.md b/plugins/postcss-custom-selectors-import-export/README.md index c9dc76703..42ed9611c 100644 --- a/plugins/postcss-custom-selectors-import-export/README.md +++ b/plugins/postcss-custom-selectors-import-export/README.md @@ -64,8 +64,8 @@ article :--heading+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. +will need to namespace custom selectors using the `customSelectors` or +`custom-selectors` key. ```js postcssCustomSelectorsImportExport({ @@ -77,14 +77,27 @@ postcssCustomSelectorsImportExport({ customSelectors: { ':--heading': 'h1, h2, h3' } }, () => { - const customProperties = { ':--heading': 'h1, h2, h3' }; + const customSelectors = { ':--heading': 'h1, h2, h3' }; - return { customProperties }; + return { customSelectors }; } ] }); ``` +### overrideImportFromWithRoot + +The `overrideImportFromWithRoot` option determines if selectors added via `importFrom` are overridden by selectors that exist in the root of your CSS. +Defaults to `false`. + +_override `importFrom` with `root`_ + +```js +postcssCustomSelectorsImportExport({ + overrideImportFromWithRoot: true +}); +``` + ### exportTo The `exportTo` option specifies destinations where custom selectors can be @@ -99,8 +112,8 @@ postcssCustomSelectorsImportExport({ 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. +need to namespace custom selectors using the `customSelectors` or +`custom-Selectors` key. ```js const cachedObject = { customSelectors: {} }; @@ -112,8 +125,8 @@ postcssCustomSelectorsImportExport({ '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' } + (customSelectors) => { + customSelectors // { ':--heading': 'h1, h2, h3' } } ] }); diff --git a/plugins/postcss-custom-selectors-import-export/docs/README.md b/plugins/postcss-custom-selectors-import-export/docs/README.md index 789ba2a59..06ee7ee76 100644 --- a/plugins/postcss-custom-selectors-import-export/docs/README.md +++ b/plugins/postcss-custom-selectors-import-export/docs/README.md @@ -51,8 +51,8 @@ article :--heading+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. +will need to namespace custom selectors using the `customSelectors` or +`custom-selectors` key. ```js ({ @@ -64,14 +64,27 @@ will need to namespace custom selectors using the `customProperties` or customSelectors: { ':--heading': 'h1, h2, h3' } }, () => { - const customProperties = { ':--heading': 'h1, h2, h3' }; + const customSelectors = { ':--heading': 'h1, h2, h3' }; - return { customProperties }; + return { customSelectors }; } ] }); ``` +### overrideImportFromWithRoot + +The `overrideImportFromWithRoot` option determines if selectors added via `importFrom` are overridden by selectors that exist in the root of your CSS. +Defaults to `true`. + +_override `importFrom` with `root`_ + +```js +({ + overrideImportFromWithRoot: false +}); +``` + ### exportTo The `exportTo` option specifies destinations where custom selectors can be @@ -86,8 +99,8 @@ passed objects. 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. +need to namespace custom selectors using the `customSelectors` or +`custom-Selectors` key. ```js const cachedObject = { customSelectors: {} }; @@ -99,8 +112,8 @@ const cachedObject = { customSelectors: {} }; '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' } + (customSelectors) => { + customSelectors // { ':--heading': 'h1, h2, h3' } } ] }); diff --git a/plugins/postcss-custom-selectors-import-export/src/import-from.js b/plugins/postcss-custom-selectors-import-export/src/import-from.js index 72fa214dd..cc46029e7 100644 --- a/plugins/postcss-custom-selectors-import-export/src/import-from.js +++ b/plugins/postcss-custom-selectors-import-export/src/import-from.js @@ -46,7 +46,7 @@ async function importCustomSelectorsFromJSONFile(from) { /* ========================================================================== */ async function importCustomSelectorsFromJSFile(from) { - const object = await import(url.pathToFileURL(path.resolve(from))); + const object = await import(url.pathToFileURL(path.resolve(from)).href); if ('default' in object) { return importCustomSelectorsFromObject(object.default); diff --git a/plugins/postcss-custom-selectors-import-export/src/index.js b/plugins/postcss-custom-selectors-import-export/src/index.js index a6d27d0c7..f9fb8fdf2 100644 --- a/plugins/postcss-custom-selectors-import-export/src/index.js +++ b/plugins/postcss-custom-selectors-import-export/src/index.js @@ -3,8 +3,7 @@ import importCustomSelectorsFromSources from './import-from'; import exportCustomSelectorsToDestinations from './export-to'; const creator = (opts) => { - // whether to preserve custom selectors and rules using them - const preserve = Boolean(Object(opts).preserve); + const overrideImportFromWithRoot = 'overrideImportFromWithRoot' in Object(opts) ? Boolean(opts.overrideImportFromWithRoot) : true; // sources to import custom selectors from const importFrom = [].concat(Object(opts).importFrom || []); @@ -17,53 +16,46 @@ const creator = (opts) => { return { postcssPlugin: 'postcss-custom-selectors-import-export', - Once: async (root, { AtRule }) => { + Once: async (root, { result, postcss }) => { const importedSelectors = await customSelectorsPromise; - const allCustomSelectors = Object.assign( - {}, - importedSelectors, - getCustomSelectors(root, { preserve }), - ); + let allCustomSelectors; + if (overrideImportFromWithRoot) { + allCustomSelectors = Object.assign( + {}, + importedSelectors, + getCustomSelectors(root), + ); + } else { + allCustomSelectors = Object.assign( + {}, + getCustomSelectors(root), + importedSelectors, + ); + } await exportCustomSelectorsToDestinations(allCustomSelectors, exportTo); if (importedSelectors) { const selectorNames = Object.keys(importedSelectors); + // Inserting in reverse order results in the correct order. selectorNames.reverse(); - const lastCustomSelector = root.nodes.find((node) => { - return node.type === 'atrule' && node.name === 'custom-selector'; - }); - - if (lastCustomSelector) { - selectorNames.forEach((selectorName) => { - lastCustomSelector.before(new AtRule({ - name: 'custom-selector', - params: `${selectorName} ${importedSelectors[selectorName].toString()}`, - source: { - input: { - from: root.input?.from ?? 'postcss-custom-selectors-import-export', - }, - start: { line: 1, column: 1 }, - end: { line: 1, column: 1 }, - }, - })); - }); - - return; + let operator = 'prepend'; + if (!overrideImportFromWithRoot) { + operator = 'append'; } selectorNames.forEach((selectorName) => { - root.prepend(new AtRule({ + root[operator](postcss.atRule({ name: 'custom-selector', params: `${selectorName} ${importedSelectors[selectorName].toString()}`, source: { input: { - from: root.input?.from ?? 'postcss-custom-selectors-import-export', + from: result.opts.from, }, - start: { line: 1, column: 1 }, - end: { line: 1, column: 1 }, + start: { offset: 0, line: 1, column: 1 }, + end: { offset: 0, line: 1, column: 1 }, }, })); }); diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css index 23fdd405c..1d1630362 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css @@ -1,3 +1,10 @@ article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-multiple-files.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from-type.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css-from.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.css.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css index 23fdd405c..1d1630362 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.empty.expect.css @@ -1,3 +1,10 @@ article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn-promise.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.fn.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.js.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.json.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css index f4b1e7127..681d94276 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.mjs.expect.css @@ -1,5 +1,14 @@ @custom-selector :--heading h1, h2, h3; + @custom-selector :--text :--heading, p; + article :--heading+p { margin-top: 0; } + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.false.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.false.expect.css new file mode 100644 index 000000000..16184231b --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.false.expect.css @@ -0,0 +1,14 @@ +article :--heading+p { + margin-top: 0; +} + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} + +@custom-selector :--text :--heading, p; + +@custom-selector :--heading h1, h2, h3 diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.true.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.true.expect.css new file mode 100644 index 000000000..681d94276 --- /dev/null +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.true.expect.css @@ -0,0 +1,14 @@ +@custom-selector :--heading h1, h2, h3; + +@custom-selector :--text :--heading, p; + +article :--heading+p { + margin-top: 0; +} + +@custom-selector :--text p, b, strong, i, em, quote, cite; +@custom-selector :--other .other; + +.anything_else { + margin-bottom: 0; +} diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css index e5852b189..1d14a4a59 100644 --- a/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css +++ b/plugins/postcss-custom-selectors-import-export/test/basic-import.with-polyfill-plugin.expect.css @@ -1,3 +1,7 @@ article :is(h1, h2, h3)+p { margin-top: 0; } + +.anything_else { + margin-bottom: 0; +} From 67d5e9d371efe2ebfc1d14f5bb979063afecd36a Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Wed, 23 Nov 2022 08:50:59 +0100 Subject: [PATCH 3/3] finish --- .../.tape.cjs | 75 ++++---- .../INSTALL.md | 77 ++++---- .../README.md | 102 +++++----- .../docs/README.md | 58 +++--- .../package.json | 8 +- .../src/custom-media-from-root.js | 17 +- .../src/custom-media-name.js | 47 ----- .../src/get-custom-media-from-imports.js | 25 ++- .../src/index.js | 67 ++++--- .../src/media-ast-from-string.js | 134 ------------- .../src/transform-atrules.js | 25 --- .../src/transform-media-list.js | 88 --------- .../test/_import.mjs | 2 +- .../test/_require.cjs | 2 +- .../test/basic.expect.css | 117 ------------ ...ss => basic.export-css-to-type.expect.css} | 92 --------- .../test/basic.export-css-to.expect.css | 132 +++++++++++++ .../test/basic.export-css.expect.css | 132 +++++++++++++ .../test/basic.export-fn-promise.expect.css | 132 +++++++++++++ .../test/basic.export-fn.expect.css | 132 +++++++++++++ .../test/basic.export-js.expect.css | 132 +++++++++++++ .../test/basic.export-json.expect.css | 132 +++++++++++++ .../test/basic.export-mjs.expect.css | 132 +++++++++++++ .../test/basic.export.expect.css | 132 +++++++++++++ .../test/complex.css | 61 ------ .../test/complex.expect.css | 49 ----- .../test/examples/.gitkeep | 0 .../test/examples/example.css | 5 - .../test/examples/example.expect.css | 3 - .../test/examples/example.preserve.expect.css | 9 - .../test/import.css-from-type.expect.css | 33 ++++ .../test/import.css-from.expect.css | 33 ++++ .../test/import.css.expect.css | 33 ++++ .../test/import.expect.css | 14 +- .../test/import.import-fn-promise.expect.css | 33 ++++ .../test/import.import-fn.expect.css | 33 ++++ ...-override-document-styles.false.expect.css | 33 ++++ ...s-override-document-styles.true.expect.css | 33 ++++ .../test/import.js.expect.css | 33 ++++ .../test/import.json.expect.css | 33 ++++ .../import.with-polyfill-plugin.expect.css | 95 ++++++++++ .../.tape.mjs | 59 +++++- .../README.md | 40 +++- .../docs/README.md | 40 +++- .../src/index.ts | 14 +- .../lib/get-custom-properties-from-imports.ts | 6 +- .../test/basic.css | 13 -- .../test/basic.export-css-to-type.expect.css | 13 -- .../test/basic.export-css-to.expect.css | 13 -- .../test/basic.export-css.expect.css | 13 -- .../test/basic.export-fn-promise.expect.css | 13 -- .../test/basic.export-fn.expect.css | 13 -- .../test/basic.export-js.expect.css | 13 -- .../test/basic.export-json.expect.css | 13 -- .../test/basic.export-mjs.expect.css | 13 -- .../test/basic.export-scss.expect.css | 13 -- .../test/basic.export.expect.css | 13 -- .../test/basic.import-cjs.expect.css | 27 +-- .../basic.import-css-from-type.expect.css | 27 +-- .../test/basic.import-css-from.expect.css | 27 +-- .../test/basic.import-css-js.expect.css | 27 +-- .../test/basic.import-css-pcss.expect.css | 27 +-- .../test/basic.import-css.expect.css | 27 +-- .../test/basic.import-fn-promise.expect.css | 27 +-- .../test/basic.import-fn.expect.css | 29 +-- ...override-document-styles.false.expect.css} | 21 +-- ...-override-document-styles.true.expect.css} | 21 +-- .../test/basic.import-is-empty.expect.css | 13 -- .../test/basic.import-json.expect.css | 27 +-- .../test/basic.import-mjs.expect.css | 27 +-- .../test/basic.import-override.expect.css | 31 +-- .../basic.import-override.inverse.expect.css | 31 +-- ...sic.import-with-polyfill-plugin.expect.css | 178 ++++++++++++++++++ .../test/basic.import.expect.css | 29 +-- .../.tape.cjs | 16 +- .../.tape.mjs | 16 +- .../README.md | 29 ++- .../docs/README.md | 31 ++- .../package.json | 6 +- .../src/import-from.js | 5 +- .../src/index.js | 10 +- ...override-document-styles.false.expect.css} | 0 ...-override-document-styles.true.expect.css} | 0 rollup/presets/package-javascript.mjs | 14 +- 84 files changed, 2175 insertions(+), 1375 deletions(-) delete mode 100644 plugins/postcss-custom-media-import-export/src/custom-media-name.js delete mode 100644 plugins/postcss-custom-media-import-export/src/media-ast-from-string.js delete mode 100644 plugins/postcss-custom-media-import-export/src/transform-atrules.js delete mode 100644 plugins/postcss-custom-media-import-export/src/transform-media-list.js delete mode 100644 plugins/postcss-custom-media-import-export/test/basic.expect.css rename plugins/postcss-custom-media-import-export/test/{basic.preserve.expect.css => basic.export-css-to-type.expect.css} (51%) create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-css-to.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-css.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-fn-promise.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-fn.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-js.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-json.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export-mjs.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/basic.export.expect.css delete mode 100644 plugins/postcss-custom-media-import-export/test/complex.css delete mode 100644 plugins/postcss-custom-media-import-export/test/complex.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/examples/.gitkeep delete mode 100644 plugins/postcss-custom-media-import-export/test/examples/example.css delete mode 100644 plugins/postcss-custom-media-import-export/test/examples/example.expect.css delete mode 100644 plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.css-from-type.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.css-from.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.css.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.import-fn-promise.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.import-fn.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.false.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.true.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.js.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.json.expect.css create mode 100644 plugins/postcss-custom-media-import-export/test/import.with-polyfill-plugin.expect.css rename plugins/postcss-custom-properties-import-export/test/{basic.preserve.expect.css => basic.import-imported-styles-override-document-styles.false.expect.css} (87%) rename plugins/postcss-custom-properties-import-export/test/{basic.expect.css => basic.import-imported-styles-override-document-styles.true.expect.css} (87%) create mode 100644 plugins/postcss-custom-properties-import-export/test/basic.import-with-polyfill-plugin.expect.css rename plugins/postcss-custom-selectors-import-export/test/{basic-import.override-import-from-with-root.true.expect.css => basic-import.imported-styles-override-document-styles.false.expect.css} (100%) rename plugins/postcss-custom-selectors-import-export/test/{basic-import.override-import-from-with-root.false.expect.css => basic-import.imported-styles-override-document-styles.true.expect.css} (100%) diff --git a/plugins/postcss-custom-media-import-export/.tape.cjs b/plugins/postcss-custom-media-import-export/.tape.cjs index 60fff7ba9..3312d9165 100644 --- a/plugins/postcss-custom-media-import-export/.tape.cjs +++ b/plugins/postcss-custom-media-import-export/.tape.cjs @@ -1,32 +1,50 @@ const postcssTape = require('../../packages/postcss-tape/dist/index.cjs'); -const plugin = require('postcss-custom-media'); +const plugin = require('@csstools/postcss-custom-media-import-export'); +const polyfillPlugin = require('postcss-custom-media'); const fs = require('fs'); postcssTape(plugin)({ - 'basic': { - message: 'supports basic usage' - }, - 'basic:preserve': { - message: 'supports { preserve: true } usage', + 'import': { + message: 'supports { importFrom: { customMedia: { ... } } } usage', options: { - preserve: true + importFrom: { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } + } } }, - 'examples/example': { - message: 'minimal example', + 'import:with-polyfill-plugin': { + message: 'works correctly together with the polyfill', + plugins: [ + plugin({ + importFrom: { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } + } + }), + polyfillPlugin(), + ] }, - 'examples/example:preserve': { - message: 'minimal example', + 'import:imported-styles-override-document-styles:true': { + message: 'supports { importedStylesOverrideDocumentStyles: true } usage', options: { - preserve: true + importedStylesOverrideDocumentStyles: true, + importFrom: { + customMedia: { + '--mq-a': '(max-width: 30em), (max-height: 30em)', + '--not-mq-a': 'not all and (--mq-a)' + } + } } }, - 'complex': { - message: 'supports complex usage' - }, - 'import': { - message: 'supports { importFrom: { customMedia: { ... } } } usage', + 'import:imported-styles-override-document-styles:false': { + message: 'supports { importedStylesOverrideDocumentStyles: false } usage', options: { + importedStylesOverrideDocumentStyles: false, importFrom: { customMedia: { '--mq-a': '(max-width: 30em), (max-height: 30em)', @@ -47,7 +65,6 @@ postcssTape(plugin)({ }; } }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:import-fn-promise': { @@ -64,7 +81,6 @@ postcssTape(plugin)({ }); } }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:json': { @@ -72,7 +88,6 @@ postcssTape(plugin)({ options: { importFrom: 'test/import-media.json' }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:js': { @@ -80,7 +95,6 @@ postcssTape(plugin)({ options: { importFrom: 'test/import-media.js' }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:css': { @@ -88,7 +102,6 @@ postcssTape(plugin)({ options: { importFrom: 'test/import-media.css' }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:css-from': { @@ -96,7 +109,6 @@ postcssTape(plugin)({ options: { importFrom: { from: 'test/import-media.css' } }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:css-from-type': { @@ -104,7 +116,6 @@ postcssTape(plugin)({ options: { importFrom: [{ from: 'test/import-media.css', type: 'css' }] }, - expect: 'import.expect.css', result: 'import.result.css' }, 'import:empty': { @@ -120,8 +131,6 @@ postcssTape(plugin)({ customMedia: null }) }, - expect: 'basic.expect.css', - result: 'basic.result.css', after() { if (__exportMediaObject.customMedia['--mq-a'] !== '(max-width: 30em), (max-height: 30em)') { throw new Error('The exportTo function failed'); @@ -137,7 +146,6 @@ postcssTape(plugin)({ } } }, - expect: 'basic.expect.css', result: 'basic.result.css' }, 'basic:export-fn-promise': { @@ -153,7 +161,6 @@ postcssTape(plugin)({ }); } }, - expect: 'basic.expect.css', result: 'basic.result.css' }, 'basic:export-json': { @@ -161,8 +168,6 @@ postcssTape(plugin)({ options: { exportTo: 'test/export-media.json' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { global.__exportMediaString = fs.readFileSync('test/export-media.json', 'utf8'); }, @@ -177,8 +182,6 @@ postcssTape(plugin)({ options: { exportTo: 'test/export-media.js' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { global.__exportMediaString = fs.readFileSync('test/export-media.js', 'utf8'); }, @@ -193,8 +196,6 @@ postcssTape(plugin)({ options: { exportTo: 'test/export-media.mjs' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { global.__exportMediaString = fs.readFileSync('test/export-media.mjs', 'utf8'); }, @@ -209,8 +210,6 @@ postcssTape(plugin)({ options: { exportTo: 'test/export-media.css' }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { global.__exportMediaString = fs.readFileSync('test/export-media.css', 'utf8'); }, @@ -225,8 +224,6 @@ postcssTape(plugin)({ options: { exportTo: { to: 'test/export-media.css' } }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { global.__exportMediaString = fs.readFileSync('test/export-media.css', 'utf8'); }, @@ -241,8 +238,6 @@ postcssTape(plugin)({ options: { exportTo: { to: 'test/export-media.css', type: 'css' } }, - expect: 'basic.expect.css', - result: 'basic.result.css', before() { global.__exportMediaString = fs.readFileSync('test/export-media.css', 'utf8'); }, diff --git a/plugins/postcss-custom-media-import-export/INSTALL.md b/plugins/postcss-custom-media-import-export/INSTALL.md index 0f3598f29..c6dc188a9 100644 --- a/plugins/postcss-custom-media-import-export/INSTALL.md +++ b/plugins/postcss-custom-media-import-export/INSTALL.md @@ -1,6 +1,6 @@ -# Installing PostCSS Custom Media +# Installing PostCSS Custom Media Import/Export -[PostCSS Custom Media] runs in all Node environments, with special instructions for: +[PostCSS Custom Media Import/Export] runs in all Node environments, with special instructions for: - [Node](#node) - [PostCSS CLI](#postcss-cli) @@ -13,20 +13,31 @@ ## Node -Add [PostCSS Custom Media] to your project: +Add [PostCSS Custom Media Import/Export] to your project: ```bash -npm install postcss postcss-custom-media --save-dev +npm install postcss @csstools/postcss-custom-media-import-export --save-dev ``` Use it as a [PostCSS] plugin: ```js +// commonjs const postcss = require('postcss'); -const postcssCustomMedia = require('postcss-custom-media'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); postcss([ - postcssCustomMedia(/* pluginOptions */) + postcssCustomMediaImportExport(/* pluginOptions */) +]).process(YOUR_CSS /*, processOptions */); +``` + +```js +// esm +import postcss from 'postcss'; +import postcssCustomMediaImportExport from '@csstools/postcss-custom-media-import-export'; + +postcss([ + postcssCustomMediaImportExport(/* pluginOptions */) ]).process(YOUR_CSS /*, processOptions */); ``` @@ -35,17 +46,17 @@ postcss([ Add [PostCSS CLI] to your project: ```bash -npm install postcss-cli postcss-custom-media --save-dev +npm install postcss-cli @csstools/postcss-custom-media-import-export --save-dev ``` -Use [PostCSS Custom Media] in your `postcss.config.js` configuration file: +Use [PostCSS Custom Media Import/Export] in your `postcss.config.js` configuration file: ```js -const postcssCustomMedia = require('postcss-custom-media'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); module.exports = { plugins: [ - postcssCustomMedia(/* pluginOptions */) + postcssCustomMediaImportExport(/* pluginOptions */) ] } ``` @@ -55,7 +66,7 @@ module.exports = { If your framework/CLI supports [`postcss-load-config`](https://github.com/postcss/postcss-load-config). ```bash -npm install postcss-custom-media --save-dev +npm install @csstools/postcss-custom-media-import-export --save-dev ``` `package.json`: @@ -64,7 +75,7 @@ npm install postcss-custom-media --save-dev { "postcss": { "plugins": { - "postcss-custom-media": {} + "@csstools/postcss-custom-media-import-export": {} } } } @@ -75,7 +86,7 @@ npm install postcss-custom-media --save-dev ```json { "plugins": { - "postcss-custom-media": {} + "@csstools/postcss-custom-media-import-export": {} } } ``` @@ -89,10 +100,10 @@ _Webpack version 5_ Add [PostCSS Loader] to your project: ```bash -npm install postcss-loader postcss-custom-media --save-dev +npm install postcss-loader @csstools/postcss-custom-media-import-export --save-dev ``` -Use [PostCSS Custom Media] in your Webpack configuration: +Use [PostCSS Custom Media Import/Export] in your Webpack configuration: ```js module.exports = { @@ -112,7 +123,7 @@ module.exports = { postcssOptions: { plugins: [ [ - "postcss-custom-media", + "@csstools/postcss-custom-media-import-export", { // Options }, @@ -133,19 +144,19 @@ module.exports = { Add [React App Rewired] and [React App Rewire PostCSS] to your project: ```bash -npm install react-app-rewired react-app-rewire-postcss postcss-custom-media --save-dev +npm install react-app-rewired react-app-rewire-postcss @csstools/postcss-custom-media-import-export --save-dev ``` -Use [React App Rewire PostCSS] and [PostCSS Custom Media] in your +Use [React App Rewire PostCSS] and [PostCSS Custom Media Import/Export] in your `config-overrides.js` file: ```js const reactAppRewirePostcss = require('react-app-rewire-postcss'); -const postcssCustomMedia = require('postcss-custom-media'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); module.exports = config => reactAppRewirePostcss(config, { plugins: () => [ - postcssCustomMedia(/* pluginOptions */) + postcssCustomMediaImportExport(/* pluginOptions */) ] }); ``` @@ -155,15 +166,15 @@ module.exports = config => reactAppRewirePostcss(config, { 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 postcss-custom-media --save-dev +npm install @csstools/postcss-custom-media-import-export --save-dev ``` -Use [PostCSS Custom Media] in your `postcss.config.json` file: +Use [PostCSS Custom Media Import/Export] in your `postcss.config.json` file: ```json { "plugins": [ - "postcss-custom-media" + "@csstools/postcss-custom-media-import-export" ] } ``` @@ -172,7 +183,7 @@ Use [PostCSS Custom Media] in your `postcss.config.json` file: { "plugins": [ [ - "postcss-custom-media", + "@csstools/postcss-custom-media-import-export", { // Optionally add plugin options } @@ -186,18 +197,18 @@ Use [PostCSS Custom Media] in your `postcss.config.json` file: Add [Gulp PostCSS] to your project: ```bash -npm install gulp-postcss postcss-custom-media --save-dev +npm install gulp-postcss @csstools/postcss-custom-media-import-export --save-dev ``` -Use [PostCSS Custom Media] in your Gulpfile: +Use [PostCSS Custom Media Import/Export] in your Gulpfile: ```js const postcss = require('gulp-postcss'); -const postcssCustomMedia = require('postcss-custom-media'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); gulp.task('css', function () { var plugins = [ - postcssCustomMedia(/* pluginOptions */) + postcssCustomMediaImportExport(/* pluginOptions */) ]; return gulp.src('./src/*.css') @@ -211,13 +222,13 @@ gulp.task('css', function () { Add [Grunt PostCSS] to your project: ```bash -npm install grunt-postcss postcss-custom-media --save-dev +npm install grunt-postcss @csstools/postcss-custom-media-import-export --save-dev ``` -Use [PostCSS Custom Media] in your Gruntfile: +Use [PostCSS Custom Media Import/Export] in your Gruntfile: ```js -const postcssCustomMedia = require('postcss-custom-media'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); grunt.loadNpmTasks('grunt-postcss'); @@ -225,7 +236,7 @@ grunt.initConfig({ postcss: { options: { processors: [ - postcssCustomMedia(/* pluginOptions */) + postcssCustomMediaImportExport(/* pluginOptions */) ] }, dist: { @@ -240,7 +251,7 @@ grunt.initConfig({ [PostCSS]: https://github.com/postcss/postcss [PostCSS CLI]: https://github.com/postcss/postcss-cli [PostCSS Loader]: https://github.com/postcss/postcss-loader -[PostCSS Custom Media]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media +[PostCSS Custom Media Import/Export]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media-import-export [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss [React App Rewired]: https://github.com/timarney/react-app-rewired [Next.js]: https://nextjs.org diff --git a/plugins/postcss-custom-media-import-export/README.md b/plugins/postcss-custom-media-import-export/README.md index ec8f4499b..7d056e66c 100644 --- a/plugins/postcss-custom-media-import-export/README.md +++ b/plugins/postcss-custom-media-import-export/README.md @@ -1,43 +1,48 @@ -# PostCSS Custom Media [PostCSS Logo][PostCSS] +# PostCSS Custom Media Import/Export [PostCSS Logo][PostCSS] -[npm version][npm-url] [CSS Standard Status][css-url] [Build Status][cli-url] [Discord][discord] +[npm version][npm-url] [Build Status][cli-url] [Discord][discord] -[PostCSS Custom Media] lets you define `@custom-media` in CSS following the [Custom Media Specification]. +[PostCSS Custom Media Import/Export] lets you import or export `@custom-media`'s into or out of your CSS. -```pcss -@custom-media --small-viewport (max-width: 30em); - -@media (--small-viewport) { - /* styles for small viewport */ -} +## As a drop in for old versions of `postcss-custom-media` -/* becomes */ +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); +const postcssCustomMedia = require('postcss-custom-media'); -@media (max-width: 30em) { - /* styles for small viewport */ -} +postcss([ + // First + postcssCustomMediaImportExport({ + /* pluginOptions */ + importedStylesOverrideDocumentStyles: false, // mimics old `postcss-custom-media` + }), + // Second + postcssCustomMedia() +]).process(YOUR_CSS /*, processOptions */); ``` ## Usage -Add [PostCSS Custom Media] to your project: +Add [PostCSS Custom Media Import/Export] to your project: ```bash -npm install postcss postcss-custom-media --save-dev +npm install postcss @csstools/postcss-custom-media-import-export --save-dev ``` Use it as a [PostCSS] plugin: ```js const postcss = require('postcss'); -const postcssCustomMedia = require('postcss-custom-media'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); postcss([ - postcssCustomMedia(/* pluginOptions */) + postcssCustomMediaImportExport(/* pluginOptions */) ]).process(YOUR_CSS /*, processOptions */); ``` -[PostCSS Custom Media] runs in all Node environments, with special +[PostCSS Custom Media Import/Export] runs in all Node environments, with special instructions for: - [Node](INSTALL.md#node) @@ -51,36 +56,6 @@ instructions for: ## Options -### preserve - -The `preserve` option determines whether the original notation -is preserved. By default, it is not preserved. - -```js -postcssCustomMedia({ preserve: true }) -``` - -```pcss -@custom-media --small-viewport (max-width: 30em); - -@media (--small-viewport) { - /* styles for small viewport */ -} - -/* becomes */ - -@custom-media --small-viewport (max-width: 30em); - -@media (max-width: 30em) { - /* styles for small viewport */ -} - -@media (--small-viewport) { - /* styles for small viewport */ -} -``` - - ### importFrom The `importFrom` option specifies sources where custom media can be imported @@ -88,16 +63,20 @@ from, which might be CSS, JS, and JSON files, functions, and directly passed objects. ```js -postcssCustomMedia({ +postcssCustomMediaImportExport({ importFrom: 'path/to/file.css' // => @custom-selector --small-viewport (max-width: 30em); }); ``` ```pcss -@media (max-width: 30em) { +@media (--small-viewport) { /* styles for small viewport */ } +/* becomes */ + +@custom-selector --small-viewport (max-width: 30em); + @media (--small-viewport) { /* styles for small viewport */ } @@ -109,7 +88,7 @@ will need to namespace custom media using the `customMedia` or `custom-media` key. ```js -postcssCustomMedia({ +postcssCustomMediaImportExport({ importFrom: [ 'path/to/file.css', 'and/then/this.js', @@ -126,6 +105,16 @@ postcssCustomMedia({ }); ``` +### importedStylesOverrideDocumentStyles + +The `importedStylesOverrideDocumentStyles` option determines if queries added via `importFrom` override queries that exist in your CSS document. +Defaults to `false`. + +```js +postcssCustomMediaImportExport({ + importedStylesOverrideDocumentStyles: true +}); + ### exportTo The `exportTo` option specifies destinations where custom media can be exported @@ -133,7 +122,7 @@ to, which might be CSS, JS, and JSON files, functions, and directly passed objects. ```js -postcssCustomMedia({ +postcssCustomMediaImportExport({ exportTo: 'path/to/file.css' // @custom-media --small-viewport (max-width: 30em); }); ``` @@ -146,7 +135,7 @@ need to namespace custom media using the `customMedia` or ```js const cachedObject = { customMedia: {} }; -postcssCustomMedia({ +postcssCustomMediaImportExport({ exportTo: [ 'path/to/file.css', // @custom-media --small-viewport (max-width: 30em); 'and/then/this.js', // module.exports = { customMedia: { '--small-viewport': '(max-width: 30em)' } } @@ -165,10 +154,9 @@ See example exports written to [CSS](test/export-media.css), [JSON](test/export-media.json). [cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test -[css-url]: https://cssdb.org/#custom-media-queries + [discord]: https://discord.gg/bUadyRwkJS -[npm-url]: https://www.npmjs.com/package/postcss-custom-media +[npm-url]: https://www.npmjs.com/package/@csstools/postcss-custom-media-import-export [PostCSS]: https://github.com/postcss/postcss -[PostCSS Custom Media]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media -[Custom Media Specification]: https://www.w3.org/TR/mediaqueries-5/#at-ruledef-custom-media +[PostCSS Custom Media Import/Export]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media-import-export diff --git a/plugins/postcss-custom-media-import-export/docs/README.md b/plugins/postcss-custom-media-import-export/docs/README.md index 9961d39fd..18b649092 100644 --- a/plugins/postcss-custom-media-import-export/docs/README.md +++ b/plugins/postcss-custom-media-import-export/docs/README.md @@ -16,14 +16,25 @@
-[] lets you define `@custom-media` in CSS following the [Custom Media Specification]. +[] lets you import or export `@custom-media`'s into or out of your CSS. -```pcss - - -/* becomes */ +## As a drop in for old versions of `postcss-custom-media` - +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomMediaImportExport = require('@csstools/postcss-custom-media-import-export'); +const postcssCustomMedia = require('postcss-custom-media'); + +postcss([ + // First + postcssCustomMediaImportExport({ + /* pluginOptions */ + importedStylesOverrideDocumentStyles: false, // mimics old `postcss-custom-media` + }), + // Second + postcssCustomMedia() +]).process(YOUR_CSS /*, processOptions */); ``` @@ -32,24 +43,6 @@ ## Options -### preserve - -The `preserve` option determines whether the original notation -is preserved. By default, it is not preserved. - -```js -({ preserve: true }) -``` - -```pcss - - -/* becomes */ - - -``` - - ### importFrom The `importFrom` option specifies sources where custom media can be imported @@ -63,10 +56,14 @@ objects. ``` ```pcss -@media (max-width: 30em) { +@media (--small-viewport) { /* styles for small viewport */ } +/* becomes */ + +@custom-selector --small-viewport (max-width: 30em); + @media (--small-viewport) { /* styles for small viewport */ } @@ -95,6 +92,16 @@ will need to namespace custom media using the `customMedia` or }); ``` +### importedStylesOverrideDocumentStyles + +The `importedStylesOverrideDocumentStyles` option determines if queries added via `importFrom` override queries that exist in your CSS document. +Defaults to `false`. + +```js +({ + importedStylesOverrideDocumentStyles: true +}); + ### exportTo The `exportTo` option specifies destinations where custom media can be exported @@ -134,4 +141,3 @@ See example exports written to [CSS](test/export-media.css), [JSON](test/export-media.json). -[Custom Media Specification]: diff --git a/plugins/postcss-custom-media-import-export/package.json b/plugins/postcss-custom-media-import-export/package.json index 0b35a255f..f72b9e5ab 100644 --- a/plugins/postcss-custom-media-import-export/package.json +++ b/plugins/postcss-custom-media-import-export/package.json @@ -26,7 +26,7 @@ "url": "https://opencollective.com/csstools" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "main": "dist/index.cjs", "module": "dist/index.mjs", @@ -47,12 +47,12 @@ "dependencies": { "postcss-value-parser": "^4.2.0" }, + "peerDependencies": { + "postcss": "^8.4" + }, "devDependencies": { "postcss-custom-media": "^9.0.1" }, - "peerDependencies": { - "postcss": "^8.2" - }, "scripts": { "build": "rollup -c ../../rollup/default.mjs", "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", diff --git a/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js b/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js index d2ddbef6a..6b64a6607 100644 --- a/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js +++ b/plugins/postcss-custom-media-import-export/src/custom-media-from-root.js @@ -1,9 +1,8 @@ -import mediaASTFromString from './media-ast-from-string'; import valueParser from 'postcss-value-parser'; -// return custom selectors from the css root, conditionally removing them -export default (root, opts) => { - // initialize custom selectors +// return custom media from the css root, conditionally removing them +export default (root) => { + // initialize custom media const customMedias = {}; // for each custom selector atrule that is a child of the css root @@ -47,15 +46,9 @@ export default (root, opts) => { } const name = paramsAst.nodes[nameNodeIndex].value.trim(); - const selectors = valueParser.stringify(paramsAst.nodes.slice(nameNodeIndex + 1)).trim(); + const mediaQueries = valueParser.stringify(paramsAst.nodes.slice(nameNodeIndex + 1)).trim(); - // write the parsed selectors to the custom selector - customMedias[name] = mediaASTFromString(selectors); - - // conditionally remove the custom selector atrule - if (!Object(opts).preserve) { - node.remove(); - } + customMedias[name] = mediaQueries; }); return customMedias; diff --git a/plugins/postcss-custom-media-import-export/src/custom-media-name.js b/plugins/postcss-custom-media-import-export/src/custom-media-name.js deleted file mode 100644 index f9a7aaa5f..000000000 --- a/plugins/postcss-custom-media-import-export/src/custom-media-name.js +++ /dev/null @@ -1,47 +0,0 @@ -import valueParser from 'postcss-value-parser'; - -export function getCustomMediaNameReference(source) { - if (!source) { - return; - } - - let paramsAst = null; - try { - paramsAst = valueParser(source); - } catch (_) { - return; - } - - if (!paramsAst || !paramsAst.nodes || !paramsAst.nodes.length) { - return; - } - - if (paramsAst.nodes.length !== 1) { - return; - } - - while (paramsAst.nodes[0].type === 'function' && paramsAst.nodes[0].value === '') { - paramsAst = paramsAst.nodes[0]; - } - - let nameNodeIndex = -1; - for (let i = 0; i < paramsAst.nodes.length; i++) { - const node = paramsAst.nodes[i]; - if (node.type === 'space' || node.type === 'comment') { - continue; - } - - if (node.type === 'word' && node.value.startsWith('--')) { - nameNodeIndex = i; - break; - } - - return; /* invalid starting node */ - } - - if (nameNodeIndex < 0) { - return; - } - - return paramsAst.nodes[nameNodeIndex].value.trim(); -} diff --git a/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js b/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js index 944795ac5..6a43c5739 100644 --- a/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js +++ b/plugins/postcss-custom-media-import-export/src/get-custom-media-from-imports.js @@ -1,17 +1,17 @@ import fs from 'fs'; import path from 'path'; +import url from 'url'; import { parse } from 'postcss'; -import getMediaAstFromMediaString from './media-ast-from-string'; import getCustomMedia from './custom-media-from-root'; /* Get Custom Media from CSS File /* ========================================================================== */ async function getCustomMediaFromCSSFile(from) { - const css = await readFile(from); + const css = await readFile(url.pathToFileURL(path.resolve(from))); const root = parse(css, { from }); - return getCustomMedia(root, { preserve: true }); + return getCustomMedia(root); } /* Get Custom Media from Object @@ -24,10 +24,6 @@ function getCustomMediaFromObject(object) { Object(object)['custom-media'], ); - for (const key in customMedia) { - customMedia[key] = getMediaAstFromMediaString(customMedia[key]); - } - return customMedia; } @@ -35,7 +31,7 @@ function getCustomMediaFromObject(object) { /* ========================================================================== */ async function getCustomMediaFromJSONFile(from) { - const object = await readJSON(from); + const object = await readJSON(url.pathToFileURL(path.resolve(from))); return getCustomMediaFromObject(object); } @@ -44,7 +40,11 @@ async function getCustomMediaFromJSONFile(from) { /* ========================================================================== */ async function getCustomMediaFromJSFile(from) { - const object = await import(from); + const object = await import(url.pathToFileURL(path.resolve(from)).href); + + if ('default' in object) { + return getCustomMediaFromObject(object.default); + } return getCustomMediaFromObject(object); } @@ -82,7 +82,12 @@ export default function getCustomMediaFromSources(sources) { return Object.assign(await customMedia, await getCustomMediaFromCSSFile(from)); } - if (type === 'js') { + if (type === 'js' || type === 'cjs') { + return Object.assign(await customMedia, await getCustomMediaFromJSFile(from)); + } + + if (type === 'mjs') { + // Only works when running as a module. return Object.assign(await customMedia, await getCustomMediaFromJSFile(from)); } diff --git a/plugins/postcss-custom-media-import-export/src/index.js b/plugins/postcss-custom-media-import-export/src/index.js index 17d08a2ed..d63d4f1a9 100644 --- a/plugins/postcss-custom-media-import-export/src/index.js +++ b/plugins/postcss-custom-media-import-export/src/index.js @@ -1,42 +1,65 @@ import getCustomMediaFromRoot from './custom-media-from-root'; import getCustomMediaFromImports from './get-custom-media-from-imports'; -import transformAtrules from './transform-atrules'; import writeCustomMediaToExports from './write-custom-media-to-exports'; -const creator = opts => { - // whether to preserve custom media and at-rules using them - const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : false; +const creator = (opts) => { + const importedStylesOverrideDocumentStyles = 'importedStylesOverrideDocumentStyles' in Object(opts) ? Boolean(opts.importedStylesOverrideDocumentStyles) : false; // sources to import custom media from const importFrom = [].concat(Object(opts).importFrom || []); - - // destinations to export custom media to + // destinations to export custom selectors to const exportTo = [].concat(Object(opts).exportTo || []); // promise any custom media are imported const customMediaImportsPromise = getCustomMediaFromImports(importFrom); - const customMediaHelperKey = Symbol('customMediaHelper'); - return { - postcssPlugin: 'postcss-custom-media', - Once: async (root, helpers) => { + postcssPlugin: 'postcss-custom-media-import-export', + Once: async (root, { result, postcss }) => { + const importedMediaQueries = await customMediaImportsPromise; - // combine rules from root and from imports - helpers[customMediaHelperKey] = Object.assign( - await customMediaImportsPromise, - getCustomMediaFromRoot(root, { preserve }), - ); - - await writeCustomMediaToExports(helpers[customMediaHelperKey], exportTo); - }, - AtRule: (atrule, helpers) => { - if (atrule.name !== 'media') { - return; + let allCustomMediaQueries; + if (importedStylesOverrideDocumentStyles) { + allCustomMediaQueries = Object.assign( + {}, + getCustomMediaFromRoot(root), + importedMediaQueries, + ); + } else { + allCustomMediaQueries = Object.assign( + {}, + importedMediaQueries, + getCustomMediaFromRoot(root), + ); } - transformAtrules(atrule, helpers[customMediaHelperKey], { preserve }); + await writeCustomMediaToExports(allCustomMediaQueries, exportTo); + + if (importedMediaQueries) { + const mediaQueryNames = Object.keys(importedMediaQueries); + // Inserting in reverse order results in the correct order. + mediaQueryNames.reverse(); + + let operator = 'prepend'; + if (importedStylesOverrideDocumentStyles) { + operator = 'append'; + } + + mediaQueryNames.forEach((mediaQueryName) => { + root[operator](postcss.atRule({ + name: 'custom-media', + params: `${mediaQueryName} ${importedMediaQueries[mediaQueryName].toString()}`, + source: { + input: { + from: result.opts.from, + }, + start: { offset: 0, line: 1, column: 1 }, + end: { offset: 0, line: 1, column: 1 }, + }, + })); + }); + } }, }; }; diff --git a/plugins/postcss-custom-media-import-export/src/media-ast-from-string.js b/plugins/postcss-custom-media-import-export/src/media-ast-from-string.js deleted file mode 100644 index 3821a319c..000000000 --- a/plugins/postcss-custom-media-import-export/src/media-ast-from-string.js +++ /dev/null @@ -1,134 +0,0 @@ -function parse(string, splitByAnd) { - const array = []; - let buffer = ''; - let split = false; - let func = 0; - let i = -1; - - while (++i < string.length) { - const char = string[i]; - - if (char === '(') { - func += 1; - } else if (char === ')') { - if (func > 0) { - func -= 1; - } - } else if (func === 0) { - if (splitByAnd && andRegExp.test(buffer + char)) { - split = true; - } else if (!splitByAnd && char === ',') { - split = true; - } - } - - if (split) { - array.push(splitByAnd ? new MediaExpression(buffer + char) : new MediaQuery(buffer)); - - buffer = ''; - split = false; - } else { - buffer += char; - } - } - - if (buffer !== '') { - array.push(splitByAnd ? new MediaExpression(buffer) : new MediaQuery(buffer)); - } - - return array; -} - -class MediaQueryList { - constructor(string) { - this.nodes = parse(string); - } - - invert() { - this.nodes.forEach(node => { - node.invert(); - }); - - return this; - } - - clone() { - return new MediaQueryList(String(this)); - } - - toString() { - return this.nodes.join(','); - } -} - -class MediaQuery { - constructor(string) { - const [, before, media, after ] = string.match(spaceWrapRegExp); - const [, modifier = '', afterModifier = ' ', type = '', beforeAnd = '', and = '', beforeExpression = '', expression1 = '', expression2 = ''] = media.match(mediaRegExp) || []; - const raws = { before, after, afterModifier, originalModifier: modifier || '', beforeAnd, and, beforeExpression }; - const nodes = parse(expression1 || expression2, true); - - Object.assign(this, { - modifier, - type, - raws, - nodes, - }); - } - - clone(overrides) { - const instance = new MediaQuery(String(this)); - - Object.assign(instance, overrides); - - return instance; - } - - invert() { - this.modifier = this.modifier ? '' : this.raws.originalModifier; - - return this; - } - - toString() { - const { raws } = this; - - return `${raws.before}${this.modifier}${this.modifier ? `${raws.afterModifier}` : ''}${this.type}${raws.beforeAnd}${raws.and}${raws.beforeExpression}${this.nodes.join('')}${this.raws.after}`; - } -} - -class MediaExpression { - constructor(string) { - const [, value, after = '', and = '', afterAnd = '' ] = string.match(andRegExp) || [null, string]; - const raws = { after, and, afterAnd }; - - Object.assign(this, { value, raws }); - } - - clone(overrides) { - const instance = new MediaExpression(String(this)); - - Object.assign(instance, overrides); - - return instance; - } - - toString() { - const { raws } = this; - - return `${this.value}${raws.after}${raws.and}${raws.afterAnd}`; - } -} - -const modifierRE = '(not|only)'; -const typeRE = '(all|print|screen|speech)'; -const noExpressionRE = '([\\W\\w]*)'; -const expressionRE = '([\\W\\w]+)'; -const noSpaceRE = '(\\s*)'; -const spaceRE = '(\\s+)'; -const andRE = '(?:(\\s+)(and))'; -const andRegExp = new RegExp(`^${expressionRE}(?:${andRE}${spaceRE})$`, 'i'); -const spaceWrapRegExp = new RegExp(`^${noSpaceRE}${noExpressionRE}${noSpaceRE}$`); -const mediaRegExp = new RegExp(`^(?:${modifierRE}${spaceRE})?(?:${typeRE}(?:${andRE}${spaceRE}${expressionRE})?|${expressionRE})$`, 'i'); - -export default string => new MediaQueryList(string); diff --git a/plugins/postcss-custom-media-import-export/src/transform-atrules.js b/plugins/postcss-custom-media-import-export/src/transform-atrules.js deleted file mode 100644 index 130e0bbb9..000000000 --- a/plugins/postcss-custom-media-import-export/src/transform-atrules.js +++ /dev/null @@ -1,25 +0,0 @@ -import transformMediaList from './transform-media-list'; -import mediaASTFromString from './media-ast-from-string'; - -// transform custom pseudo selectors with custom selectors -export default (atrule, customMedia, { preserve }) => { - if (atrule.params.indexOf('--') > -1) { - const mediaAST = mediaASTFromString(atrule.params); - const params = String(transformMediaList(mediaAST, customMedia)); - if (params === null) { - return; - } - - if (params === atrule.params) { - return; - } - - atrule.cloneBefore({ - params: params, - }); - - if (!preserve) { - atrule.remove(); - } - } -}; diff --git a/plugins/postcss-custom-media-import-export/src/transform-media-list.js b/plugins/postcss-custom-media-import-export/src/transform-media-list.js deleted file mode 100644 index 2eed88fdb..000000000 --- a/plugins/postcss-custom-media-import-export/src/transform-media-list.js +++ /dev/null @@ -1,88 +0,0 @@ -import { getCustomMediaNameReference } from './custom-media-name'; - -// return transformed medias, replacing custom pseudo medias with custom medias -export default function transformMediaList(mediaList, customMedias) { - let index = mediaList.nodes.length - 1; - - while (index >= 0) { - const transformedMedias = transformMedia(mediaList.nodes[index], customMedias); - - if (transformedMedias.length) { - mediaList.nodes.splice(index, 1, ...transformedMedias); - } - - --index; - } - - return mediaList; -} - -// return custom pseudo medias replaced with custom medias -function transformMedia(media, customMedias) { - const transpiledMedias = []; - - for (const index in media.nodes) { - const { value, nodes } = media.nodes[index]; - const key = getCustomMediaNameReference(value); - if (key && (key in customMedias)) { - for (const replacementMedia of customMedias[key].nodes) { - // use the first available modifier unless they cancel each other out - const modifier = media.modifier !== replacementMedia.modifier - ? media.modifier || replacementMedia.modifier - : ''; - const mediaClone = media.clone({ - modifier, - // conditionally use the raws from the first available modifier - raws: !modifier || media.modifier - ? { ...media.raws } - : { ...replacementMedia.raws }, - type: media.type || replacementMedia.type, - }); - - // conditionally include more replacement raws when the type is present - if (mediaClone.type === replacementMedia.type) { - Object.assign(mediaClone.raws, { - and: replacementMedia.raws.and, - beforeAnd: replacementMedia.raws.beforeAnd, - beforeExpression: replacementMedia.raws.beforeExpression, - }); - } - - mediaClone.nodes.splice(index, 1, ...replacementMedia.clone().nodes.map(node => { - // use raws and spacing from the current usage - if (media.nodes[index].raws.and) { - node.raws = { ...media.nodes[index].raws }; - } - - node.spaces = { ...media.nodes[index].spaces }; - - return node; - })); - - // remove the currently transformed key to prevent recursion - const nextCustomMedia = getCustomMediasWithoutKey(customMedias, key); - const retranspiledMedias = transformMedia(mediaClone, nextCustomMedia); - - if (retranspiledMedias.length) { - transpiledMedias.push(...retranspiledMedias); - } else { - transpiledMedias.push(mediaClone); - } - } - - return transpiledMedias; - } else if (nodes && nodes.length) { - transformMediaList(media.nodes[index], customMedias); - } - } - - return transpiledMedias; -} - -const getCustomMediasWithoutKey = (customMedias, key) => { - const nextCustomMedias = Object.assign({}, customMedias); - - delete nextCustomMedias[key]; - - return nextCustomMedias; -}; diff --git a/plugins/postcss-custom-media-import-export/test/_import.mjs b/plugins/postcss-custom-media-import-export/test/_import.mjs index e5c94d2ab..0b023a196 100644 --- a/plugins/postcss-custom-media-import-export/test/_import.mjs +++ b/plugins/postcss-custom-media-import-export/test/_import.mjs @@ -1,5 +1,5 @@ import assert from 'assert'; -import plugin from 'postcss-custom-media'; +import plugin from '@csstools/postcss-custom-media-import-export'; plugin(); assert.ok(plugin.postcss, 'should have "postcss flag"'); diff --git a/plugins/postcss-custom-media-import-export/test/_require.cjs b/plugins/postcss-custom-media-import-export/test/_require.cjs index 8c9e7f530..8af6094ba 100644 --- a/plugins/postcss-custom-media-import-export/test/_require.cjs +++ b/plugins/postcss-custom-media-import-export/test/_require.cjs @@ -1,5 +1,5 @@ const assert = require('assert'); -const plugin = require('postcss-custom-media'); +const plugin = require('@csstools/postcss-custom-media-import-export'); plugin(); assert.ok(plugin.postcss, 'should have "postcss flag"'); diff --git a/plugins/postcss-custom-media-import-export/test/basic.expect.css b/plugins/postcss-custom-media-import-export/test/basic.expect.css deleted file mode 100644 index ed797941c..000000000 --- a/plugins/postcss-custom-media-import-export/test/basic.expect.css +++ /dev/null @@ -1,117 +0,0 @@ -@media (max-width: 30em),(max-height: 30em) { - body { - order: 1; - } -} - -@media screen and (max-width: 30em) { - body { - order: 1; - } -} - -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1; - } -} - -@media not all and (max-width: 30em),not all and (max-height: 30em) { - body { - order: 2; - } -} - -@media not all and (max-width: 30em),not all and (max-height: 30em) { - body { - order: 1; - } -} - -@media all and (max-width: 30em),all and (max-height: 30em) { - body { - order: 2; - } -} - -@media (--circular-mq-a) { - body { - order: 3; - } -} - -@media (--circular-mq-b) { - body { - order: 4; - } -} - -@media (--unresolved-mq) { - body { - order: 5; - } -} - -@media (min-width: 320px) and (max-width: 640px) { - body { - order: 6; - } -} - -@media (min-width: 320px) and (max-width: 640px) { - body { - order: 7; - } -} - -@media (min-width: 320px) and (max-width: 640px) and (min-aspect-ratio: 16/9) { - body { - order: 8; - } -} - -@media (max-width: 30em),(max-height: 30em) { - body { - order: 1000; - } -} - -@media (max-width: 30em),(max-height: 30em) { - body { - order: 1001; - } -} - -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1002; - } -} - -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1003; - } -} - -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1004; - } -} - -@media (max-width: 30em),(max-height: 30em), -(max-width: 30em), -(max-height: 30em) { - body { - order: 1005; - } -} - -@media (trailer--) { - body { - order: 1006; - } -} - -@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-css-to-type.expect.css similarity index 51% rename from plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css rename to plugins/postcss-custom-media-import-export/test/basic.export-css-to-type.expect.css index 4c8aefc63..a992cfe2f 100644 --- a/plugins/postcss-custom-media-import-export/test/basic.preserve.expect.css +++ b/plugins/postcss-custom-media-import-export/test/basic.export-css-to-type.expect.css @@ -2,72 +2,36 @@ @custom-media --mq-b screen and (max-width: 30em); @custom-media --not-mq-a not all and (--mq-a); -@media (max-width: 30em),(max-height: 30em) { - body { - order: 1; - } -} - @media (--mq-a) { body { order: 1; } } -@media screen and (max-width: 30em) { - body { - order: 1; - } -} - @media (--mq-b) { body { order: 1; } } -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1; - } -} - @media (--mq-a), (--mq-a) { body { order: 1; } } -@media not all and (max-width: 30em),not all and (max-height: 30em) { - body { - order: 2; - } -} - @media not all and (--mq-a) { body { order: 2; } } -@media not all and (max-width: 30em),not all and (max-height: 30em) { - body { - order: 1; - } -} - @media (--not-mq-a) { body { order: 1; } } -@media all and (max-width: 30em),all and (max-height: 30em) { - body { - order: 2; - } -} - @media not all and (--not-mq-a) { body { order: 2; @@ -98,12 +62,6 @@ @custom-media --min (min-width: 320px); @custom-media --max (max-width: 640px); -@media (min-width: 320px) and (max-width: 640px) { - body { - order: 6; - } -} - @media (--min) and (--max) { body { order: 6; @@ -112,98 +70,48 @@ @custom-media --concat (min-width: 320px) and (max-width: 640px); -@media (min-width: 320px) and (max-width: 640px) { - body { - order: 7; - } -} - @media (--concat) { body { order: 7; } } -@media (min-width: 320px) and (max-width: 640px) and (min-aspect-ratio: 16/9) { - body { - order: 8; - } -} - @media (--concat) and (min-aspect-ratio: 16/9) { body { order: 8; } } -@media (max-width: 30em),(max-height: 30em) { - body { - order: 1000; - } -} - @media ( --mq-a ) { body { order: 1000; } } -@media (max-width: 30em),(max-height: 30em) { - body { - order: 1001; - } -} - @media ( --mq-a ) { body { order: 1001; } } -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1002; - } -} - @media ( --mq-a ), ( --mq-a ) { body { order: 1002; } } -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1003; - } -} - @media ( --mq-a ), ( --mq-a ) { body { order: 1003; } } -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { - body { - order: 1004; - } -} - @media ( --mq-a ), ( --mq-a ) { body { order: 1004; } } -@media (max-width: 30em),(max-height: 30em), -(max-width: 30em), -(max-height: 30em) { - body { - order: 1005; - } -} - @media ( --mq-a ), diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-css-to.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-css-to.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-css-to.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-css.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-css.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-css.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-fn-promise.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-fn-promise.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-fn-promise.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-fn.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-fn.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-fn.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-js.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-js.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-js.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-json.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-json.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-json.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export-mjs.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export-mjs.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export-mjs.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/basic.export.expect.css b/plugins/postcss-custom-media-import-export/test/basic.export.expect.css new file mode 100644 index 000000000..a992cfe2f --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/basic.export.expect.css @@ -0,0 +1,132 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); +@custom-media --mq-b screen and (max-width: 30em); +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-b) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --circular-mq-a (--circular-mq-b); +@custom-media --circular-mq-b (--circular-mq-a); + +@media (--circular-mq-a) { + body { + order: 3; + } +} + +@media (--circular-mq-b) { + body { + order: 4; + } +} + +@media (--unresolved-mq) { + body { + order: 5; + } +} + +@custom-media --min (min-width: 320px); +@custom-media --max (max-width: 640px); + +@media (--min) and (--max) { + body { + order: 6; + } +} + +@custom-media --concat (min-width: 320px) and (max-width: 640px); + +@media (--concat) { + body { + order: 7; + } +} + +@media (--concat) and (min-aspect-ratio: 16/9) { + body { + order: 8; + } +} + +@media ( --mq-a ) { + body { + order: 1000; + } +} + +@media ( --mq-a ) { + body { + order: 1001; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1002; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1003; + } +} + +@media ( --mq-a ), ( --mq-a ) { + body { + order: 1004; + } +} + +@media ( + --mq-a +), +( + --mq-a +) { + body { + order: 1005; + } +} + +@media (trailer--) { + body { + order: 1006; + } +} + +@custom-media trailer-- (min-width: 320px); diff --git a/plugins/postcss-custom-media-import-export/test/complex.css b/plugins/postcss-custom-media-import-export/test/complex.css deleted file mode 100644 index 4b427cbd0..000000000 --- a/plugins/postcss-custom-media-import-export/test/complex.css +++ /dev/null @@ -1,61 +0,0 @@ -@custom-media --🧑🏾‍🎤 (min-width: 1); - -@media (--🧑🏾‍🎤) { - .a { - order: 1; - } -} - -@custom-media --\(\)-escaped (min-width: 2); - -@media (--\(\)-escaped) { - .a { - order: 2; - } -} - -@custom-media --modern (min-width: 3), (min-width: 4); - -@media (--modern) and (width > 1024px) { - .a { order: 3; } -} - -/* #region https://github.com/csstools/postcss-custom-media/issues/51 */ -/* TODO: This is broken at the moment */ -@custom-media --screen only screen; -@custom-media --md-and-larger1 --screen and (width >= 570px); -@custom-media --md-and-larger2 (--screen) and (width >= 570px); -@custom-media --md-and-larger3 only screen and (width >= 570px); -@custom-media --md-larger4 (width >=570px); -@custom-media --md-smaller4 (width < 1000px); - -@media (--md-and-larger1) { - body { - background-color: red; - } -} - -@media (--md-and-larger2) { - body { - background-color: yellow; - } -} - -@media (--md-and-larger3) { - body { - background-color: green; - } -} - -@media (--screen) and (--md-larger4) { - body { - background-color: green; - } -} - -@media (--md-larger4) and (--md-smaller4) { - body { - background-color: green; - } -} -/* #endregion https://github.com/csstools/postcss-custom-media/issues/51 */ diff --git a/plugins/postcss-custom-media-import-export/test/complex.expect.css b/plugins/postcss-custom-media-import-export/test/complex.expect.css deleted file mode 100644 index 486d7f515..000000000 --- a/plugins/postcss-custom-media-import-export/test/complex.expect.css +++ /dev/null @@ -1,49 +0,0 @@ -@media (min-width: 1) { - .a { - order: 1; - } -} - -@media (min-width: 2) { - .a { - order: 2; - } -} - -@media (min-width: 3) and (width > 1024px),(min-width: 4) and (width > 1024px) { - .a { order: 3; } -} - -/* #region https://github.com/csstools/postcss-custom-media/issues/51 */ -/* TODO: This is broken at the moment */ - -@media only screen(width >= 570px) { - body { - background-color: red; - } -} - -@media only screen(width >= 570px) { - body { - background-color: yellow; - } -} - -@media only screen and (width >= 570px) { - body { - background-color: green; - } -} - -@media only screen(width >=570px) { - body { - background-color: green; - } -} - -@media (width >=570px) and (width < 1000px) { - body { - background-color: green; - } -} -/* #endregion https://github.com/csstools/postcss-custom-media/issues/51 */ diff --git a/plugins/postcss-custom-media-import-export/test/examples/.gitkeep b/plugins/postcss-custom-media-import-export/test/examples/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/postcss-custom-media-import-export/test/examples/example.css b/plugins/postcss-custom-media-import-export/test/examples/example.css deleted file mode 100644 index fe7634abb..000000000 --- a/plugins/postcss-custom-media-import-export/test/examples/example.css +++ /dev/null @@ -1,5 +0,0 @@ -@custom-media --small-viewport (max-width: 30em); - -@media (--small-viewport) { - /* styles for small viewport */ -} diff --git a/plugins/postcss-custom-media-import-export/test/examples/example.expect.css b/plugins/postcss-custom-media-import-export/test/examples/example.expect.css deleted file mode 100644 index 3b68ef9be..000000000 --- a/plugins/postcss-custom-media-import-export/test/examples/example.expect.css +++ /dev/null @@ -1,3 +0,0 @@ -@media (max-width: 30em) { - /* styles for small viewport */ -} diff --git a/plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css b/plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css deleted file mode 100644 index ed814500b..000000000 --- a/plugins/postcss-custom-media-import-export/test/examples/example.preserve.expect.css +++ /dev/null @@ -1,9 +0,0 @@ -@custom-media --small-viewport (max-width: 30em); - -@media (max-width: 30em) { - /* styles for small viewport */ -} - -@media (--small-viewport) { - /* styles for small viewport */ -} diff --git a/plugins/postcss-custom-media-import-export/test/import.css-from-type.expect.css b/plugins/postcss-custom-media-import-export/test/import.css-from-type.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.css-from-type.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.css-from.expect.css b/plugins/postcss-custom-media-import-export/test/import.css-from.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.css-from.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.css.expect.css b/plugins/postcss-custom-media-import-export/test/import.css.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.css.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.expect.css b/plugins/postcss-custom-media-import-export/test/import.expect.css index 0bc2bbf3b..6bbae6719 100644 --- a/plugins/postcss-custom-media-import-export/test/import.expect.css +++ b/plugins/postcss-custom-media-import-export/test/import.expect.css @@ -1,28 +1,32 @@ -@media (max-width: 30em),(max-height: 30em) { +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { body { order: 1; } } -@media (max-width: 30em),(max-height: 30em), (max-width: 30em), (max-height: 30em) { +@media (--mq-a), (--mq-a) { body { order: 1; } } -@media not all and (max-width: 30em),not all and (max-height: 30em) { +@media not all and (--mq-a) { body { order: 2; } } -@media not all and (max-width: 30em),not all and (max-height: 30em) { +@media (--not-mq-a) { body { order: 1; } } -@media all and (max-width: 30em),all and (max-height: 30em) { +@media not all and (--not-mq-a) { body { order: 2; } diff --git a/plugins/postcss-custom-media-import-export/test/import.import-fn-promise.expect.css b/plugins/postcss-custom-media-import-export/test/import.import-fn-promise.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.import-fn-promise.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.import-fn.expect.css b/plugins/postcss-custom-media-import-export/test/import.import-fn.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.import-fn.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.false.expect.css b/plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.false.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.false.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.true.expect.css b/plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.true.expect.css new file mode 100644 index 000000000..5b32dc079 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.imported-styles-override-document-styles.true.expect.css @@ -0,0 +1,33 @@ +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} + +@custom-media --not-mq-a not all and (--mq-a); + +@custom-media --mq-a (max-width: 30em), (max-height: 30em) diff --git a/plugins/postcss-custom-media-import-export/test/import.js.expect.css b/plugins/postcss-custom-media-import-export/test/import.js.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.js.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.json.expect.css b/plugins/postcss-custom-media-import-export/test/import.json.expect.css new file mode 100644 index 000000000..6bbae6719 --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.json.expect.css @@ -0,0 +1,33 @@ +@custom-media --mq-a (max-width: 30em), (max-height: 30em); + +@custom-media --not-mq-a not all and (--mq-a); + +@media (--mq-a) { + body { + order: 1; + } +} + +@media (--mq-a), (--mq-a) { + body { + order: 1; + } +} + +@media not all and (--mq-a) { + body { + order: 2; + } +} + +@media (--not-mq-a) { + body { + order: 1; + } +} + +@media not all and (--not-mq-a) { + body { + order: 2; + } +} diff --git a/plugins/postcss-custom-media-import-export/test/import.with-polyfill-plugin.expect.css b/plugins/postcss-custom-media-import-export/test/import.with-polyfill-plugin.expect.css new file mode 100644 index 000000000..e28c85d2a --- /dev/null +++ b/plugins/postcss-custom-media-import-export/test/import.with-polyfill-plugin.expect.css @@ -0,0 +1,95 @@ +@media (max-width: 30em),(max-height: 30em) { + body { + order: 1; + } +} + +@media (max-width: 30em),(max-height: 30em),(max-width: 30em),(max-height: 30em) { + body { + order: 1; + } +} + +@media (max-width: 30em),(max-height: 30em) { + +@media not all and (max-color:2147477350) { + body { + order: 2; + } +} +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + +@media not all and (color:2147477350) { + body { + order: 2; + } +} +} + +@media (max-width: 30em),(max-height: 30em) { + +@media not all and (max-color:2147477350) { + body { + order: 1; + } +} +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + +@media not all and (color:2147477350) { + body { + order: 1; + } +} +} + +@media (max-width: 30em),(max-height: 30em) { + +@media not all and (max-color:2147477350) { + +@media not all and (max-color:2147477350) { + body { + order: 2; + } +} +} +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + +@media not all and (color:2147477350) { + +@media not all and (max-color:2147477350) { + body { + order: 2; + } +} +} +} + +@media (max-width: 30em),(max-height: 30em) { + +@media all and (max-color:2147477350) { + +@media not all and (color:2147477350) { + body { + order: 2; + } +} +} +} + +@media not all and (max-width: 30em),not all and (max-height: 30em) { + +@media all and (color:2147477350) { + +@media not all and (color:2147477350) { + body { + order: 2; + } +} +} +} diff --git a/plugins/postcss-custom-properties-import-export/.tape.mjs b/plugins/postcss-custom-properties-import-export/.tape.mjs index dfa935e93..09d68a303 100644 --- a/plugins/postcss-custom-properties-import-export/.tape.mjs +++ b/plugins/postcss-custom-properties-import-export/.tape.mjs @@ -1,22 +1,61 @@ import postcssTape from '../../packages/postcss-tape/dist/index.mjs'; import plugin from '@csstools/postcss-custom-properties-import-export'; +import polyfillPlugin from 'postcss-custom-properties'; import { strict as assert } from 'assert'; import postcssImport from 'postcss-import'; import fs from 'fs'; postcssTape(plugin)({ - 'basic': { - message: 'supports basic usage' + 'basic:import': { + message: 'supports { importFrom: { customProperties: { ... } } } usage', + options: { + importFrom: { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--z-index': 10 + } + } + } }, - 'basic:preserve': { - message: 'supports { preserve: false } usage', + 'basic:import-with-polyfill-plugin': { + message: 'works correctly together with the polyfill', + plugins: [ + plugin({ + importFrom: { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--z-index': 10 + } + } + }), + polyfillPlugin(), + ] + }, + 'basic:import-imported-styles-override-document-styles:true': { + message: 'supports { importedStylesOverrideDocumentStyles: true } usage', options: { - preserve: false + importedStylesOverrideDocumentStyles: true, + importFrom: { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--z-index': 10 + } + } } }, - 'basic:import': { - message: 'supports { importFrom: { customProperties: { ... } } } usage', + 'basic:import-imported-styles-override-document-styles:false': { + message: 'supports { importedStylesOverrideDocumentStyles: false } usage', options: { + importedStylesOverrideDocumentStyles: false, importFrom: { customProperties: { '--color': 'rgb(255, 0, 0)', @@ -131,7 +170,7 @@ postcssTape(plugin)({ }, }, 'basic:import-override': { - message: 'importFrom with { preserve: false } should override root properties', + message: 'importFrom with { preserve: false } should override importFrom properties', options: { preserve: false, importFrom: { @@ -147,10 +186,10 @@ postcssTape(plugin)({ }, }, 'basic:import-override:inverse': { - message: 'importFrom with { preserve: false, overrideImportFromWithRoot: true } should override importFrom properties', + message: 'importFrom with { preserve: false, importedStylesOverrideDocumentStyles: true } should override root properties', options: { preserve: false, - overrideImportFromWithRoot: true, + importedStylesOverrideDocumentStyles: true, importFrom: { customProperties: { '--color': 'rgb(0, 0, 0)', diff --git a/plugins/postcss-custom-properties-import-export/README.md b/plugins/postcss-custom-properties-import-export/README.md index 5d2b1f898..bf23b2773 100644 --- a/plugins/postcss-custom-properties-import-export/README.md +++ b/plugins/postcss-custom-properties-import-export/README.md @@ -4,6 +4,29 @@ [PostCSS Custom Properties Import/Export] lets you import or export CSS custom properties (`--foo: pink;`) into or out of your CSS. +## As a drop in for old versions of `postcss-custom-properties` + +⚠️ `postcss-custom-properties` no longer removes any custom properties. +If you inject a lot of properties they will all be added to your final CSS. +Use a separate CSS minifier/optimizer to remove unused custom properties. + +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); +const postcssCustomProperties = require('postcss-custom-properties'); + +postcss([ + // First + postcssCustomPropertiesImportExport({ + /* pluginOptions */ + importedStylesOverrideDocumentStyles: true, // mimics old `postcss-custom-properties` + }), + // Second + postcssCustomProperties() +]).process(YOUR_CSS /*, processOptions */); +``` + ## Usage Add [PostCSS Custom Properties Import/Export] to your project: @@ -56,13 +79,13 @@ article { /* becomes */ -article { - color: var(--color); -} - :root { --color: var(rgb(245 20 255)); } + +article { + color: var(--color); +} ``` Multiple sources can be passed into this option, and they will be parsed in the @@ -88,18 +111,15 @@ postcssCustomPropertiesImportExport({ }); ``` -### overrideImportFromWithRoot +### importedStylesOverrideDocumentStyles -The `overrideImportFromWithRoot` option determines if properties added via `importFrom` are overridden by properties that exist in `:root`. +The `importedStylesOverrideDocumentStyles` option determines if properties added via `importFrom` override properties that exist in your CSS document. Defaults to `false`. -_override `importFrom` with `root`_ - ```js postcssCustomPropertiesImportExport({ - overrideImportFromWithRoot: true + importedStylesOverrideDocumentStyles: true }); -``` ### exportTo diff --git a/plugins/postcss-custom-properties-import-export/docs/README.md b/plugins/postcss-custom-properties-import-export/docs/README.md index b2f26dbba..3df3822e4 100644 --- a/plugins/postcss-custom-properties-import-export/docs/README.md +++ b/plugins/postcss-custom-properties-import-export/docs/README.md @@ -18,6 +18,29 @@ [] lets you import or export CSS custom properties (`--foo: pink;`) into or out of your CSS. +## As a drop in for old versions of `postcss-custom-properties` + +⚠️ `postcss-custom-properties` no longer removes any custom properties. +If you inject a lot of properties they will all be added to your final CSS. +Use a separate CSS minifier/optimizer to remove unused custom properties. + +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomPropertiesImportExport = require('@csstools/postcss-custom-properties-import-export'); +const postcssCustomProperties = require('postcss-custom-properties'); + +postcss([ + // First + postcssCustomPropertiesImportExport({ + /* pluginOptions */ + importedStylesOverrideDocumentStyles: true, // mimics old `postcss-custom-properties` + }), + // Second + postcssCustomProperties() +]).process(YOUR_CSS /*, processOptions */); +``` + @@ -43,13 +66,13 @@ article { /* becomes */ -article { - color: var(--color); -} - :root { --color: var(rgb(245 20 255)); } + +article { + color: var(--color); +} ``` Multiple sources can be passed into this option, and they will be parsed in the @@ -75,18 +98,15 @@ will need to namespace custom properties using the `customProperties` or }); ``` -### overrideImportFromWithRoot +### importedStylesOverrideDocumentStyles -The `overrideImportFromWithRoot` option determines if properties added via `importFrom` are overridden by properties that exist in `:root`. +The `importedStylesOverrideDocumentStyles` option determines if properties added via `importFrom` override properties that exist in your CSS document. Defaults to `false`. -_override `importFrom` with `root`_ - ```js ({ - overrideImportFromWithRoot: true + importedStylesOverrideDocumentStyles: true }); -``` ### exportTo diff --git a/plugins/postcss-custom-properties-import-export/src/index.ts b/plugins/postcss-custom-properties-import-export/src/index.ts index 90977e8f9..96ff55c0c 100644 --- a/plugins/postcss-custom-properties-import-export/src/index.ts +++ b/plugins/postcss-custom-properties-import-export/src/index.ts @@ -12,11 +12,11 @@ export interface PluginOptions { exportTo?: ExportOptions | Array /** Specifies if `importFrom` properties or `:root` properties have priority. */ - overrideImportFromWithRoot?: boolean + importedStylesOverrideDocumentStyles?: boolean } const creator: PluginCreator = (opts?: PluginOptions) => { - const overrideImportFromWithRoot = 'overrideImportFromWithRoot' in Object(opts) ? Boolean(opts.overrideImportFromWithRoot) : false; + const importedStylesOverrideDocumentStyles = 'importedStylesOverrideDocumentStyles' in Object(opts) ? Boolean(opts.importedStylesOverrideDocumentStyles) : false; // sources to import custom selectors from let importFrom: Array = []; @@ -38,7 +38,7 @@ const creator: PluginCreator = (opts?: PluginOptions) => { const customPropertiesPromise = getCustomPropertiesFromImports(importFrom); return { - postcssPlugin: 'postcss-custom-properties', + postcssPlugin: 'postcss-custom-properties-import-export', prepare () { const customPropertiesForInsertion: Map = new Map(); const customPropertiesForExport: Map = new Map(); @@ -52,12 +52,12 @@ const creator: PluginCreator = (opts?: PluginOptions) => { customPropertiesForInsertion.set(name, value); } - if (overrideImportFromWithRoot) { - for (const [name, value] of [...importedCustomerProperties, ...rootCustomProperties]) { + if (importedStylesOverrideDocumentStyles) { + for (const [name, value] of [...rootCustomProperties, ...importedCustomerProperties]) { customPropertiesForExport.set(name, value); } } else { - for (const [name, value] of [...rootCustomProperties, ...importedCustomerProperties]) { + for (const [name, value] of [...importedCustomerProperties, ...rootCustomProperties]) { customPropertiesForExport.set(name, value); } } @@ -70,7 +70,7 @@ const creator: PluginCreator = (opts?: PluginOptions) => { propertyNames.reverse(); let operator = 'prepend'; - if (!overrideImportFromWithRoot) { + if (importedStylesOverrideDocumentStyles) { operator = 'append'; } diff --git a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts index 9e9cab8a5..a1f354e03 100644 --- a/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts +++ b/plugins/postcss-custom-properties-import-export/src/lib/get-custom-properties-from-imports.ts @@ -9,7 +9,7 @@ import { promises as fsp } from 'fs'; /* ========================================================================== */ async function getCustomPropertiesFromCSSFile(from) { - const css = await fsp.readFile(pathToFileURL(from)); + const css = await fsp.readFile(pathToFileURL(path.resolve(from))); const root = parse(css, { from : from.toString() }); return getCustomPropertiesFromRoot(root); @@ -40,7 +40,7 @@ function getCustomPropertiesFromObject(object: ImportCustomProperties): Map> { - const object = await readJSON(pathToFileURL(from)); + const object = await readJSON(pathToFileURL(path.resolve(from))); return getCustomPropertiesFromObject(object); } @@ -49,7 +49,7 @@ async function getCustomPropertiesFromJSONFile(from): Promise> { - const object = await import(pathToFileURL(from).href); + const object = await import(pathToFileURL(path.resolve(from)).href); if ('default' in object) { return getCustomPropertiesFromObject(object.default); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.css b/plugins/postcss-custom-properties-import-export/test/basic.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to-type.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-css-to.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-css.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-fn-promise.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-fn.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-js.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-json.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-mjs.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export-scss.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.export.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-cjs.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from-type.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-from.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-js.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css-pcss.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-css.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css index 29a8a4cf4..998feb186 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-fn-promise.expect.css @@ -1,3 +1,10 @@ +:root { + --z-index: 10; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --z-index: 10; - --ref-color: var(--color); - --color-2: yellow; - --color: rgb(255, 0, 0); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css index 42909d79c..d3cc2c38c 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-fn.expect.css @@ -1,3 +1,11 @@ +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} + html { --ref-color: skip; } @@ -28,19 +36,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,11 +153,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --z-index: 10; - --margin: 0 10px 20px 30px; - --ref-color: var(--color); - --color-2: yellow; - --color: rgb(255, 0, 0); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-imported-styles-override-document-styles.false.expect.css similarity index 87% rename from plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css rename to plugins/postcss-custom-properties-import-export/test/basic.import-imported-styles-override-document-styles.false.expect.css index 8715e58b1..d3cc2c38c 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.preserve.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-imported-styles-override-document-styles.false.expect.css @@ -1,3 +1,11 @@ +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} + html { --ref-color: skip; } @@ -28,19 +36,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-imported-styles-override-document-styles.true.expect.css similarity index 87% rename from plugins/postcss-custom-properties-import-export/test/basic.expect.css rename to plugins/postcss-custom-properties-import-export/test/basic.import-imported-styles-override-document-styles.true.expect.css index 8715e58b1..9669ff981 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-imported-styles-override-document-styles.true.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,3 +145,11 @@ html { right: var(--✅-size); right: 2em; } + +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css index 8715e58b1..dc9fd373e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-is-empty.expect.css @@ -28,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css index 29a8a4cf4..998feb186 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-json.expect.css @@ -1,3 +1,10 @@ +:root { + --z-index: 10; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --z-index: 10; - --ref-color: var(--color); - --color-2: yellow; - --color: rgb(255, 0, 0); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css index 2aa3d2ae5..4f0cd5aa0 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-mjs.expect.css @@ -1,3 +1,10 @@ +:root { + --color-2: yellow; + --color: rgb(255, 0, 0); + --z-index: 10; + --ref-color: var(--color); +} + html { --ref-color: skip; } @@ -28,19 +35,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,10 +152,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --color-2: yellow; - --color: rgb(255, 0, 0); - --z-index: 10; - --ref-color: var(--color); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css index 0c591d8e8..3e1d78f8e 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-override.expect.css @@ -1,3 +1,12 @@ +:root { + --z-index: 10; + --shadow-color: rgb(0,0,0); + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(0, 0, 0); +} + html { --ref-color: skip; } @@ -28,19 +37,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,12 +154,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --z-index: 10; - --shadow-color: rgb(0,0,0); - --margin: 0 10px 20px 30px; - --ref-color: var(--color); - --color-2: yellow; - --color: rgb(0, 0, 0); -} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css index ffa824579..383fd0036 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-override.inverse.expect.css @@ -1,12 +1,3 @@ -:root { - --z-index: 10; - --shadow-color: rgb(0,0,0); - --margin: 0 10px 20px 30px; - --ref-color: var(--color); - --color-2: yellow; - --color: rgb(0, 0, 0); -} - html { --ref-color: skip; } @@ -37,19 +28,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -167,3 +145,12 @@ html { right: var(--✅-size); right: 2em; } + +:root { + --z-index: 10; + --shadow-color: rgb(0,0,0); + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(0, 0, 0); +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import-with-polyfill-plugin.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import-with-polyfill-plugin.expect.css new file mode 100644 index 000000000..c6836c647 --- /dev/null +++ b/plugins/postcss-custom-properties-import-export/test/basic.import-with-polyfill-plugin.expect.css @@ -0,0 +1,178 @@ +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} + +html { + --ref-color: skip; +} + +:root { + --color: rgb(255, 0, 0); + --color-h: 0; + --color-s: 100%; + --color-l: 50%; + --color-hsl: hsl(var(--color-h), var(--color-s), var(--color-l)); + --ref-color: var(--color); + --circular: var(--circular-2); + --circular-2: var(--circular); + --margin: 0 10px 20px 30px; + --shadow-color: rgb(255,0,0); + --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); + --font-family: "Open Sans", sans-serif; + --url-1: url("/my/path"); + --url-2: url('/my/path'); + --url-3: url(/my/path); + --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; + color: rgb(255, 0, 0); + color: var(--color); +} + +:root, +[data-theme=light] { + --theme-color: #053; +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); + color: var(--override, var(--color)); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); + box-shadow: inset 0 -3px 0 var(--color); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; + margin: var(--margin); +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); + box-shadow: var(--shadow); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; + font-family: var(--font-family); +} + +.test--fallback { + color: yellow; + color: var(--color-2, blue); +} + +.test--color_w_var { + color: rgb(255, 0, 0); + color: var(--ref-color); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); + color: var(--color-hsl); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: 10; + z-index: var(--z-index); +} + +.test--nested-fallback { + z-index: 1; + z-index: var(--xxx, var(--yyy, 1)); +} + +.text--calc { + width: calc((100% - 1px) + 10px); + width: calc((100% - var(--xxx, 1px)) + var(--yyy, 10px)); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; + color: var( + --color, + blue + )/*rtl:red*/; +} + +.test--combined-selector { + color: #053; + color: var(--theme-color); +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); + background: var(--url-1); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); + background: var(--url-2); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); + background: var(--url-3); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + background: var(--url-4); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} + +.test { + font-family: "Helvetica Neue", Arial, sans-serif; + font-family: var(--font, "Helvetica Neue", Arial, sans-serif); +} + +.ignores-declarations-that-have-an-exact-fallback-a { + left: 1rem; + left: var(--does-not-exist, 1rem); +} + +.ignores-declarations-that-have-an-exact-fallback-b { + right: 2em; + right: var(--✅-size); +} + +.does-not-ignore-declarations-that-have-an-exact-override-a { + left: 1rem; + left: var(--does-not-exist, 1rem); + left: 1rem; +} + +.does-not-ignore-declarations-that-have-an-exact-override-b { + right: 2em; + right: var(--✅-size); + right: 2em; +} diff --git a/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css b/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css index 42909d79c..d3cc2c38c 100644 --- a/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css +++ b/plugins/postcss-custom-properties-import-export/test/basic.import.expect.css @@ -1,3 +1,11 @@ +:root { + --z-index: 10; + --margin: 0 10px 20px 30px; + --ref-color: var(--color); + --color-2: yellow; + --color: rgb(255, 0, 0); +} + html { --ref-color: skip; } @@ -28,19 +36,6 @@ html { --theme-color: #053; } -.ignore-line { - /* postcss-custom-properties: ignore next */ - color: var(--color); - background-color: var(--color-2, blue); -} - -.ignore-block { - /* postcss-custom-properties: off */ - color: var(--color-2, blue); - box-shadow: inset 0 -3px 0 var(--color); - background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); -} - .test { --skip: gray; color: var(--override, var(--color)); @@ -158,11 +153,3 @@ html { right: var(--✅-size); right: 2em; } - -:root { - --z-index: 10; - --margin: 0 10px 20px 30px; - --ref-color: var(--color); - --color-2: yellow; - --color: rgb(255, 0, 0); -} diff --git a/plugins/postcss-custom-selectors-import-export/.tape.cjs b/plugins/postcss-custom-selectors-import-export/.tape.cjs index 0ab5de880..4d0f0d15a 100644 --- a/plugins/postcss-custom-selectors-import-export/.tape.cjs +++ b/plugins/postcss-custom-selectors-import-export/.tape.cjs @@ -16,10 +16,10 @@ postcssTape(plugin)({ } } }, - 'basic-import:override-import-from-with-root:true': { - message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + 'basic-import:imported-styles-override-document-styles:true': { + message: 'supports { importedStylesOverrideDocumentStyles: true, importFrom: { customSelectors: { ... } } } usage', options: { - overrideImportFromWithRoot: true, + importedStylesOverrideDocumentStyles: true, importFrom: { customSelectors: { ':--heading': 'h1, h2, h3', @@ -27,16 +27,16 @@ postcssTape(plugin)({ } }, exportTo(customProperties) { - if (customProperties[':--text'] !== 'p, b, strong, i, em, quote, cite') { + if (customProperties[':--text'] !== ':--heading, p') { throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); } } } }, - 'basic-import:override-import-from-with-root:false': { - message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + 'basic-import:imported-styles-override-document-styles:false': { + message: 'supports { importedStylesOverrideDocumentStyles: false, importFrom: { customSelectors: { ... } } } usage', options: { - overrideImportFromWithRoot: false, + importedStylesOverrideDocumentStyles: false, importFrom: { customSelectors: { ':--heading': 'h1, h2, h3', @@ -44,7 +44,7 @@ postcssTape(plugin)({ } }, exportTo(customProperties) { - if (customProperties[':--text'] !== ':--heading, p') { + if (customProperties[':--text'] !== 'p, b, strong, i, em, quote, cite') { throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); } } diff --git a/plugins/postcss-custom-selectors-import-export/.tape.mjs b/plugins/postcss-custom-selectors-import-export/.tape.mjs index 4aa2bc59e..e71d51823 100644 --- a/plugins/postcss-custom-selectors-import-export/.tape.mjs +++ b/plugins/postcss-custom-selectors-import-export/.tape.mjs @@ -32,10 +32,10 @@ postcssTape(plugin)({ polyfillPlugin(), ] }, - 'basic-import:override-import-from-with-root:true': { - message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + 'basic-import:imported-styles-override-document-styles:true': { + message: 'supports { importedStylesOverrideDocumentStyles: true, importFrom: { customSelectors: { ... } } } usage', options: { - overrideImportFromWithRoot: true, + importedStylesOverrideDocumentStyles: true, importFrom: { customSelectors: { ':--heading': 'h1, h2, h3', @@ -43,16 +43,16 @@ postcssTape(plugin)({ } }, exportTo(customProperties) { - if (customProperties[':--text'] !== 'p, b, strong, i, em, quote, cite') { + if (customProperties[':--text'] !== ':--heading, p') { throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); } } } }, - 'basic-import:override-import-from-with-root:false': { - message: 'supports { overrideImportFromWithRoot: true, importFrom: { customSelectors: { ... } } } usage', + 'basic-import:imported-styles-override-document-styles:false': { + message: 'supports { importedStylesOverrideDocumentStyles: false, importFrom: { customSelectors: { ... } } } usage', options: { - overrideImportFromWithRoot: false, + importedStylesOverrideDocumentStyles: false, importFrom: { customSelectors: { ':--heading': 'h1, h2, h3', @@ -60,7 +60,7 @@ postcssTape(plugin)({ } }, exportTo(customProperties) { - if (customProperties[':--text'] !== ':--heading, p') { + if (customProperties[':--text'] !== 'p, b, strong, i, em, quote, cite') { throw new Error('Incorrect value in exportTo ' + customProperties[':--text']); } } diff --git a/plugins/postcss-custom-selectors-import-export/README.md b/plugins/postcss-custom-selectors-import-export/README.md index 42ed9611c..ed5382e8a 100644 --- a/plugins/postcss-custom-selectors-import-export/README.md +++ b/plugins/postcss-custom-selectors-import-export/README.md @@ -4,6 +4,25 @@ [PostCSS Custom Selectors Import/Export] lets you import or export `@custom-selector`'s into or out of your CSS. +## As a drop in for old versions of `postcss-custom-selectors` + +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); +const postcssCustomSelectors = require('postcss-custom-selectors'); + +postcss([ + // First + postcssCustomSelectorsImportExport({ + /* pluginOptions */ + importedStylesOverrideDocumentStyles: false, // mimics old `postcss-custom-selectors` + }), + // Second + postcssCustomSelectors() +]).process(YOUR_CSS /*, processOptions */); +``` + ## Usage Add [PostCSS Custom Selectors Import/Export] to your project: @@ -57,6 +76,7 @@ article :--heading + p { /* becomes */ @custom-selector :--heading h1, h2, h3; + article :--heading+p { margin-top: 0; } @@ -85,18 +105,15 @@ postcssCustomSelectorsImportExport({ }); ``` -### overrideImportFromWithRoot +### importedStylesOverrideDocumentStyles -The `overrideImportFromWithRoot` option determines if selectors added via `importFrom` are overridden by selectors that exist in the root of your CSS. +The `importedStylesOverrideDocumentStyles` option determines if selectors added via `importFrom` override selectors that exist in your CSS document. Defaults to `false`. -_override `importFrom` with `root`_ - ```js postcssCustomSelectorsImportExport({ - overrideImportFromWithRoot: true + importedStylesOverrideDocumentStyles: true }); -``` ### exportTo diff --git a/plugins/postcss-custom-selectors-import-export/docs/README.md b/plugins/postcss-custom-selectors-import-export/docs/README.md index 06ee7ee76..0f0ef1624 100644 --- a/plugins/postcss-custom-selectors-import-export/docs/README.md +++ b/plugins/postcss-custom-selectors-import-export/docs/README.md @@ -18,6 +18,25 @@ [] lets you import or export `@custom-selector`'s into or out of your CSS. +## As a drop in for old versions of `postcss-custom-selectors` + +```js +// commonjs +const postcss = require('postcss'); +const postcssCustomSelectorsImportExport = require('@csstools/postcss-custom-selectors-import-export'); +const postcssCustomSelectors = require('postcss-custom-selectors'); + +postcss([ + // First + postcssCustomSelectorsImportExport({ + /* pluginOptions */ + importedStylesOverrideDocumentStyles: false, // mimics old `postcss-custom-selectors` + }), + // Second + postcssCustomSelectors() +]).process(YOUR_CSS /*, processOptions */); +``` + @@ -44,6 +63,7 @@ article :--heading + p { /* becomes */ @custom-selector :--heading h1, h2, h3; + article :--heading+p { margin-top: 0; } @@ -72,18 +92,15 @@ will need to namespace custom selectors using the `customSelectors` or }); ``` -### overrideImportFromWithRoot - -The `overrideImportFromWithRoot` option determines if selectors added via `importFrom` are overridden by selectors that exist in the root of your CSS. -Defaults to `true`. +### importedStylesOverrideDocumentStyles -_override `importFrom` with `root`_ +The `importedStylesOverrideDocumentStyles` option determines if selectors added via `importFrom` override selectors that exist in your CSS document. +Defaults to `false`. ```js ({ - overrideImportFromWithRoot: false + importedStylesOverrideDocumentStyles: true }); -``` ### exportTo diff --git a/plugins/postcss-custom-selectors-import-export/package.json b/plugins/postcss-custom-selectors-import-export/package.json index cd1034639..43259f05d 100644 --- a/plugins/postcss-custom-selectors-import-export/package.json +++ b/plugins/postcss-custom-selectors-import-export/package.json @@ -49,12 +49,12 @@ "dependencies": { "postcss-selector-parser": "^6.0.4" }, - "devDependencies": { - "postcss-custom-selectors": "^7.0.0" - }, "peerDependencies": { "postcss": "^8.4" }, + "devDependencies": { + "postcss-custom-selectors": "^7.0.0" + }, "scripts": { "build": "rollup -c ../../rollup/default.mjs", "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"", diff --git a/plugins/postcss-custom-selectors-import-export/src/import-from.js b/plugins/postcss-custom-selectors-import-export/src/import-from.js index cc46029e7..915fdf28b 100644 --- a/plugins/postcss-custom-selectors-import-export/src/import-from.js +++ b/plugins/postcss-custom-selectors-import-export/src/import-from.js @@ -89,15 +89,16 @@ export default function importCustomSelectorsFromSources(sources) { return Object.assign(customSelectors, importCustomSelectorsFromCSSAST(from)); } - if (type === 'css') { + if (type === 'css' || type === 'pcss') { return Object.assign(customSelectors, await importCustomSelectorsFromCSSFile(from)); } - if (type === 'js') { + if (type === 'js' || type === 'cjs') { return Object.assign(customSelectors, await importCustomSelectorsFromJSFile(from)); } if (type === 'mjs') { + // Only works when running as a module. return Object.assign(customSelectors, await importCustomSelectorsFromJSFile(from)); } diff --git a/plugins/postcss-custom-selectors-import-export/src/index.js b/plugins/postcss-custom-selectors-import-export/src/index.js index f9fb8fdf2..9783c5f9d 100644 --- a/plugins/postcss-custom-selectors-import-export/src/index.js +++ b/plugins/postcss-custom-selectors-import-export/src/index.js @@ -3,7 +3,7 @@ import importCustomSelectorsFromSources from './import-from'; import exportCustomSelectorsToDestinations from './export-to'; const creator = (opts) => { - const overrideImportFromWithRoot = 'overrideImportFromWithRoot' in Object(opts) ? Boolean(opts.overrideImportFromWithRoot) : true; + const importedStylesOverrideDocumentStyles = 'importedStylesOverrideDocumentStyles' in Object(opts) ? Boolean(opts.importedStylesOverrideDocumentStyles) : false; // sources to import custom selectors from const importFrom = [].concat(Object(opts).importFrom || []); @@ -20,17 +20,17 @@ const creator = (opts) => { const importedSelectors = await customSelectorsPromise; let allCustomSelectors; - if (overrideImportFromWithRoot) { + if (importedStylesOverrideDocumentStyles) { allCustomSelectors = Object.assign( {}, - importedSelectors, getCustomSelectors(root), + importedSelectors, ); } else { allCustomSelectors = Object.assign( {}, - getCustomSelectors(root), importedSelectors, + getCustomSelectors(root), ); } @@ -42,7 +42,7 @@ const creator = (opts) => { selectorNames.reverse(); let operator = 'prepend'; - if (!overrideImportFromWithRoot) { + if (importedStylesOverrideDocumentStyles) { operator = 'append'; } diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.true.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.imported-styles-override-document-styles.false.expect.css similarity index 100% rename from plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.true.expect.css rename to plugins/postcss-custom-selectors-import-export/test/basic-import.imported-styles-override-document-styles.false.expect.css diff --git a/plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.false.expect.css b/plugins/postcss-custom-selectors-import-export/test/basic-import.imported-styles-override-document-styles.true.expect.css similarity index 100% rename from plugins/postcss-custom-selectors-import-export/test/basic-import.override-import-from-with-root.false.expect.css rename to plugins/postcss-custom-selectors-import-export/test/basic-import.imported-styles-override-document-styles.true.expect.css diff --git a/rollup/presets/package-javascript.mjs b/rollup/presets/package-javascript.mjs index 2c852e52d..986f3e0a9 100644 --- a/rollup/presets/package-javascript.mjs +++ b/rollup/presets/package-javascript.mjs @@ -18,13 +18,13 @@ export function packageJavascript() { exclude: 'node_modules/**', presets: packageBabelPreset, }), - // terser({ - // compress: { - // reduce_funcs: false, // https://github.com/terser/terser/issues/1305 - // }, - // keep_classnames: true, - // keep_fnames: true, - // }), + terser({ + compress: { + reduce_funcs: false, // https://github.com/terser/terser/issues/1305 + }, + keep_classnames: true, + keep_fnames: true, + }), ], }, ];