From c0db55d604e362e36a4d6656ea1e2eb7ce1c96a1 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Fri, 11 Feb 2022 12:56:06 +0000
Subject: [PATCH 01/33] initial commit
---
plugins/postcss-cascade-layers/.gitignore | 6 +
plugins/postcss-cascade-layers/.nvmrc | 1 +
plugins/postcss-cascade-layers/.tape.mjs | 17 +++
plugins/postcss-cascade-layers/CHANGELOG.md | 1 +
plugins/postcss-cascade-layers/INSTALL.md | 3 +
plugins/postcss-cascade-layers/LICENSE.md | 108 ++++++++++++++++++
plugins/postcss-cascade-layers/README.md | 5 +
plugins/postcss-cascade-layers/package.json | 55 +++++++++
plugins/postcss-cascade-layers/src/cli.ts | 16 +++
plugins/postcss-cascade-layers/src/index.ts | 21 ++++
.../postcss-cascade-layers/stryker.conf.json | 19 +++
.../postcss-cascade-layers/test/_import.mjs | 6 +
.../postcss-cascade-layers/test/_require.cjs | 6 +
.../test/basic.color.expect.css | 7 ++
plugins/postcss-cascade-layers/test/basic.css | 7 ++
.../test/basic.expect.css | 7 ++
.../test/cli/basic.color.expect.css | 5 +
.../postcss-cascade-layers/test/cli/basic.css | 3 +
.../test/cli/basic.expect.css | 5 +
.../test/cli/basic.external-map.expect.css | 5 +
.../cli/basic.external-map.expect.css.map | 1 +
.../test/cli/basic.failure.expect.css | 0
.../test/cli/basic.no-map.expect.css | 3 +
.../test/cli/basic.replace.css | 5 +
.../test/cli/basic.replace.expect.css | 5 +
.../test/cli/basic.stdin.expect.css | 5 +
.../postcss-cascade-layers/test/cli/out/a.css | 5 +
.../test/cli/out/a.css.map | 1 +
.../test/cli/out/a.expect.css | 5 +
.../test/cli/out/a.expect.css.map | 1 +
.../postcss-cascade-layers/test/cli/out/b.css | 5 +
.../test/cli/out/b.css.map | 1 +
.../test/cli/out/b.expect.css | 5 +
.../test/cli/out/b.expect.css.map | 1 +
.../test/cli/out/concatenated.css | 6 +
.../test/cli/out/concatenated.expect.css | 6 +
.../postcss-cascade-layers/test/cli/src/a.css | 3 +
.../postcss-cascade-layers/test/cli/src/b.css | 3 +
.../postcss-cascade-layers/test/cli/test.sh | 94 +++++++++++++++
.../postcss-cascade-layers/test/example.css | 7 ++
.../test/example.expect.css | 7 ++
plugins/postcss-cascade-layers/tsconfig.json | 9 ++
42 files changed, 481 insertions(+)
create mode 100644 plugins/postcss-cascade-layers/.gitignore
create mode 100644 plugins/postcss-cascade-layers/.nvmrc
create mode 100644 plugins/postcss-cascade-layers/.tape.mjs
create mode 100644 plugins/postcss-cascade-layers/CHANGELOG.md
create mode 100644 plugins/postcss-cascade-layers/INSTALL.md
create mode 100644 plugins/postcss-cascade-layers/LICENSE.md
create mode 100644 plugins/postcss-cascade-layers/README.md
create mode 100644 plugins/postcss-cascade-layers/package.json
create mode 100644 plugins/postcss-cascade-layers/src/cli.ts
create mode 100644 plugins/postcss-cascade-layers/src/index.ts
create mode 100644 plugins/postcss-cascade-layers/stryker.conf.json
create mode 100644 plugins/postcss-cascade-layers/test/_import.mjs
create mode 100644 plugins/postcss-cascade-layers/test/_require.cjs
create mode 100644 plugins/postcss-cascade-layers/test/basic.color.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/basic.css
create mode 100644 plugins/postcss-cascade-layers/test/basic.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.color.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.failure.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.replace.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.css.map
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.css.map
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/concatenated.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/src/a.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/src/b.css
create mode 100644 plugins/postcss-cascade-layers/test/cli/test.sh
create mode 100644 plugins/postcss-cascade-layers/test/example.css
create mode 100644 plugins/postcss-cascade-layers/test/example.expect.css
create mode 100644 plugins/postcss-cascade-layers/tsconfig.json
diff --git a/plugins/postcss-cascade-layers/.gitignore b/plugins/postcss-cascade-layers/.gitignore
new file mode 100644
index 000000000..7172b04f1
--- /dev/null
+++ b/plugins/postcss-cascade-layers/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+package-lock.json
+yarn.lock
+*.result.css
+*.result.css.map
+dist/*
diff --git a/plugins/postcss-cascade-layers/.nvmrc b/plugins/postcss-cascade-layers/.nvmrc
new file mode 100644
index 000000000..f0b10f153
--- /dev/null
+++ b/plugins/postcss-cascade-layers/.nvmrc
@@ -0,0 +1 @@
+v16.13.1
diff --git a/plugins/postcss-cascade-layers/.tape.mjs b/plugins/postcss-cascade-layers/.tape.mjs
new file mode 100644
index 000000000..d75456c1e
--- /dev/null
+++ b/plugins/postcss-cascade-layers/.tape.mjs
@@ -0,0 +1,17 @@
+import postcssTape from '../../packages/postcss-tape/dist/index.mjs';
+import plugin from '@csstools/postcss-cascade-layers';
+
+postcssTape(plugin)({
+ basic: {
+ message: "supports basic usage",
+ },
+ 'basic:color': {
+ message: "supports { color: '' }",
+ options: {
+ color: 'purple'
+ }
+ },
+ example: {
+ message: "minimal example",
+ },
+});
diff --git a/plugins/postcss-cascade-layers/CHANGELOG.md b/plugins/postcss-cascade-layers/CHANGELOG.md
new file mode 100644
index 000000000..2475e1207
--- /dev/null
+++ b/plugins/postcss-cascade-layers/CHANGELOG.md
@@ -0,0 +1 @@
+# Changes to PostCSS Base Plugin
diff --git a/plugins/postcss-cascade-layers/INSTALL.md b/plugins/postcss-cascade-layers/INSTALL.md
new file mode 100644
index 000000000..ceacefa74
--- /dev/null
+++ b/plugins/postcss-cascade-layers/INSTALL.md
@@ -0,0 +1,3 @@
+# Installing PostCSS Base Plugin
+
+
diff --git a/plugins/postcss-cascade-layers/LICENSE.md b/plugins/postcss-cascade-layers/LICENSE.md
new file mode 100644
index 000000000..0bc1fa706
--- /dev/null
+++ b/plugins/postcss-cascade-layers/LICENSE.md
@@ -0,0 +1,108 @@
+# CC0 1.0 Universal
+
+## Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an “owner”) of an original work of
+authorship and/or a database (each, a “Work”).
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific works
+(“Commons”) that the public can reliably and without fear of later claims of
+infringement build upon, modify, incorporate in other works, reuse and
+redistribute as freely as possible in any form whatsoever and for any purposes,
+including without limitation commercial purposes. These owners may contribute
+to the Commons to promote the ideal of a free culture and the further
+production of creative, cultural and scientific works, or to gain reputation or
+greater distribution for their Work in part through the use and efforts of
+others.
+
+For these and/or other purposes and motivations, and without any expectation of
+additional consideration or compensation, the person associating CC0 with a
+Work (the “Affirmer”), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and
+publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+ protected by copyright and related or neighboring rights (“Copyright and
+ Related Rights”). Copyright and Related Rights include, but are not limited
+ to, the following:
+ 1. the right to reproduce, adapt, distribute, perform, display, communicate,
+ and translate a Work;
+ 2. moral rights retained by the original author(s) and/or performer(s);
+ 3. publicity and privacy rights pertaining to a person’s image or likeness
+ depicted in a Work;
+ 4. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(i), below;
+ 5. rights protecting the extraction, dissemination, use and reuse of data in
+ a Work;
+ 6. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation thereof,
+ including any amended or successor version of such directive); and
+ 7. other similar, equivalent or corresponding rights throughout the world
+ based on applicable law or treaty, and any national implementations
+ thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+ applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+ unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright
+ and Related Rights and associated claims and causes of action, whether now
+ known or unknown (including existing as well as future claims and causes of
+ action), in the Work (i) in all territories worldwide, (ii) for the maximum
+ duration provided by applicable law or treaty (including future time
+ extensions), (iii) in any current or future medium and for any number of
+ copies, and (iv) for any purpose whatsoever, including without limitation
+ commercial, advertising or promotional purposes (the “Waiver”). Affirmer
+ makes the Waiver for the benefit of each member of the public at large and
+ to the detriment of Affirmer’s heirs and successors, fully intending that
+ such Waiver shall not be subject to revocation, rescission, cancellation,
+ termination, or any other legal or equitable action to disrupt the quiet
+ enjoyment of the Work by the public as contemplated by Affirmer’s express
+ Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+ judged legally invalid or ineffective under applicable law, then the Waiver
+ shall be preserved to the maximum extent permitted taking into account
+ Affirmer’s express Statement of Purpose. In addition, to the extent the
+ Waiver is so judged Affirmer hereby grants to each affected person a
+ royalty-free, non transferable, non sublicensable, non exclusive,
+ irrevocable and unconditional license to exercise Affirmer’s Copyright and
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
+ maximum duration provided by applicable law or treaty (including future time
+ extensions), (iii) in any current or future medium and for any number of
+ copies, and (iv) for any purpose whatsoever, including without limitation
+ commercial, advertising or promotional purposes (the “License”). The License
+ shall be deemed effective as of the date CC0 was applied by Affirmer to the
+ Work. Should any part of the License for any reason be judged legally
+ invalid or ineffective under applicable law, such partial invalidity or
+ ineffectiveness shall not invalidate the remainder of the License, and in
+ such case Affirmer hereby affirms that he or she will not (i) exercise any
+ of his or her remaining Copyright and Related Rights in the Work or (ii)
+ assert any associated claims and causes of action with respect to the Work,
+ in either case contrary to Affirmer’s express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+ 1. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ 2. Affirmer offers the Work as-is and makes no representations or warranties
+ of any kind concerning the Work, express, implied, statutory or
+ otherwise, including without limitation warranties of title,
+ merchantability, fitness for a particular purpose, non infringement, or
+ the absence of latent or other defects, accuracy, or the present or
+ absence of errors, whether or not discoverable, all to the greatest
+ extent permissible under applicable law.
+ 3. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person’s Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the Work.
+ 4. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to this
+ CC0 or use of the Work.
+
+For more information, please see
+http://creativecommons.org/publicdomain/zero/1.0/.
diff --git a/plugins/postcss-cascade-layers/README.md b/plugins/postcss-cascade-layers/README.md
new file mode 100644
index 000000000..a631e79c7
--- /dev/null
+++ b/plugins/postcss-cascade-layers/README.md
@@ -0,0 +1,5 @@
+# PostCSS Base Plugin [
][postcss]
+
+
+
+[postcss]: https://github.com/postcss/postcss
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
new file mode 100644
index 000000000..12d3de10d
--- /dev/null
+++ b/plugins/postcss-cascade-layers/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "@csstools/postcss-cascade-layers",
+ "private": true,
+ "version": "0.0.0",
+ "description": "A base plugin",
+ "author": "Jonathan Neal ",
+ "license": "CC0-1.0",
+ "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"
+ ],
+ "bin": {
+ "postcss-base-plugin": "dist/cli.cjs"
+ },
+ "scripts": {
+ "build": "rollup -c ../../rollup/default.js",
+ "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"",
+ "lint": "eslint ./src --ext .js --ext .ts --ext .mjs --no-error-on-unmatched-pattern",
+ "prepublishOnly": "npm run clean && npm run build && npm run test",
+ "stryker": "stryker run --logLevel error",
+ "test": "node .tape.mjs && npm run test:exports",
+ "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs",
+ "test:cli": "bash ./test/cli/test.sh",
+ "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ },
+ "keywords": [
+ "postcss-plugin"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/csstools/postcss-plugins.git",
+ "directory": "plugins/postcss-cascade-layers"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ }
+}
diff --git a/plugins/postcss-cascade-layers/src/cli.ts b/plugins/postcss-cascade-layers/src/cli.ts
new file mode 100644
index 000000000..f087923cf
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/cli.ts
@@ -0,0 +1,16 @@
+import plugin from './index';
+import { cli, helpTextLogger } from '@csstools/base-cli';
+
+cli(
+ plugin,
+ ['color', 'another_option'],
+ helpTextLogger(
+ 'postcss-cascade-layers',
+ 'Base Plugin',
+ 'An example plugin CLI',
+ {
+ color: 'A CSS color',
+ another_option: true,
+ },
+ ),
+);
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
new file mode 100644
index 000000000..ee1f96bc0
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -0,0 +1,21 @@
+import type { PluginCreator } from 'postcss';
+
+const creator: PluginCreator<{ color: string }> = (opts?: { color: string }) => {
+ return {
+ postcssPlugin: 'postcss-base-plugin',
+ Declaration(decl) {
+ if (decl.value === 'red') {
+ if (opts && opts.color) {
+ decl.value = opts.color;
+ } else {
+ decl.value = 'blue';
+ }
+ }
+ },
+ };
+};
+
+creator.postcss = true;
+
+export default creator;
+
diff --git a/plugins/postcss-cascade-layers/stryker.conf.json b/plugins/postcss-cascade-layers/stryker.conf.json
new file mode 100644
index 000000000..091dc740c
--- /dev/null
+++ b/plugins/postcss-cascade-layers/stryker.conf.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "../../node_modules/@stryker-mutator/core/schema/stryker-schema.json",
+ "mutate": [
+ "src/**/*.ts"
+ ],
+ "buildCommand": "npm run build",
+ "testRunner": "command",
+ "coverageAnalysis": "perTest",
+ "tempDirName": "../../.stryker-tmp",
+ "commandRunner": {
+ "command": "npm run test:tape"
+ },
+ "thresholds": {
+ "high": 100,
+ "low": 100,
+ "break": 100
+ },
+ "inPlace": true
+}
diff --git a/plugins/postcss-cascade-layers/test/_import.mjs b/plugins/postcss-cascade-layers/test/_import.mjs
new file mode 100644
index 000000000..0e4c84832
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/_import.mjs
@@ -0,0 +1,6 @@
+import assert from 'assert';
+import plugin from '@csstools/postcss-base-plugin';
+plugin();
+
+assert.ok(plugin.postcss, 'should have "postcss flag"');
+assert.equal(typeof plugin, 'function', 'should return a function');
diff --git a/plugins/postcss-cascade-layers/test/_require.cjs b/plugins/postcss-cascade-layers/test/_require.cjs
new file mode 100644
index 000000000..8b21a7929
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/_require.cjs
@@ -0,0 +1,6 @@
+const assert = require('assert');
+const plugin = require('@csstools/postcss-base-plugin');
+plugin();
+
+assert.ok(plugin.postcss, 'should have "postcss flag"');
+assert.equal(typeof plugin, 'function', 'should return a function');
diff --git a/plugins/postcss-cascade-layers/test/basic.color.expect.css b/plugins/postcss-cascade-layers/test/basic.color.expect.css
new file mode 100644
index 000000000..041b1dabf
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/basic.color.expect.css
@@ -0,0 +1,7 @@
+.foo {
+ color: purple;
+}
+
+.baz {
+ color: green;
+}
diff --git a/plugins/postcss-cascade-layers/test/basic.css b/plugins/postcss-cascade-layers/test/basic.css
new file mode 100644
index 000000000..181f83a54
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/basic.css
@@ -0,0 +1,7 @@
+.foo {
+ color: red;
+}
+
+.baz {
+ color: green;
+}
diff --git a/plugins/postcss-cascade-layers/test/basic.expect.css b/plugins/postcss-cascade-layers/test/basic.expect.css
new file mode 100644
index 000000000..9d738d5ac
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/basic.expect.css
@@ -0,0 +1,7 @@
+.foo {
+ color: blue;
+}
+
+.baz {
+ color: green;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.color.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.color.expect.css
new file mode 100644
index 000000000..93f6b5280
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.color.expect.css
@@ -0,0 +1,5 @@
+.foo {
+ color: purple;
+}
+
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtDQUNDLGFBQVU7QUFDWCIsImZpbGUiOiJiYXNpYy5jb2xvci5yZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.css b/plugins/postcss-cascade-layers/test/cli/basic.css
new file mode 100644
index 000000000..cedf0a6d1
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.css
@@ -0,0 +1,3 @@
+.foo {
+ color: red;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.expect.css
new file mode 100644
index 000000000..3f6f0d3a4
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.expect.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtDQUNDLFdBQVU7QUFDWCIsImZpbGUiOiJiYXNpYy5yZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css
new file mode 100644
index 000000000..b8aaa92b1
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=basic.external-map.result.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map b/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map
new file mode 100644
index 000000000..8f8e733cc
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../../stdin"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"basic.external-map.result.css","sourcesContent":[".foo {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.failure.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.failure.expect.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css
new file mode 100644
index 000000000..c47fa33b8
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css
@@ -0,0 +1,3 @@
+.foo {
+ color: blue;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.replace.css b/plugins/postcss-cascade-layers/test/cli/basic.replace.css
new file mode 100644
index 000000000..49bc11102
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.replace.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLnJlcGxhY2UuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0NBQ0MsV0FBVTtBQUNYIiwiZmlsZSI6ImJhc2ljLnJlcGxhY2UuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css
new file mode 100644
index 000000000..49bc11102
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLnJlcGxhY2UuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0NBQ0MsV0FBVTtBQUNYIiwiZmlsZSI6ImJhc2ljLnJlcGxhY2UuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css
new file mode 100644
index 000000000..7def14035
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0NBQ0MsV0FBVTtBQUNYIiwiZmlsZSI6InN0ZG91dCIsInNvdXJjZXNDb250ZW50IjpbIi5mb28ge1xuXHRjb2xvcjogcmVkO1xufVxuIl19 */
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.css b/plugins/postcss-cascade-layers/test/cli/out/a.css
new file mode 100644
index 000000000..60e2001c0
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/a.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=a.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.css.map b/plugins/postcss-cascade-layers/test/cli/out/a.css.map
new file mode 100644
index 000000000..d5c8b6f40
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/a.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/a.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"a.css","sourcesContent":[".foo {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.expect.css b/plugins/postcss-cascade-layers/test/cli/out/a.expect.css
new file mode 100644
index 000000000..60e2001c0
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/a.expect.css
@@ -0,0 +1,5 @@
+.foo {
+ color: blue;
+}
+
+/*# sourceMappingURL=a.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map b/plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map
new file mode 100644
index 000000000..d5c8b6f40
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/a.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"a.css","sourcesContent":[".foo {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.css b/plugins/postcss-cascade-layers/test/cli/out/b.css
new file mode 100644
index 000000000..ac091faeb
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/b.css
@@ -0,0 +1,5 @@
+.baz {
+ color: blue;
+}
+
+/*# sourceMappingURL=b.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.css.map b/plugins/postcss-cascade-layers/test/cli/out/b.css.map
new file mode 100644
index 000000000..0a97ca290
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/b.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/b.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"b.css","sourcesContent":[".baz {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.expect.css b/plugins/postcss-cascade-layers/test/cli/out/b.expect.css
new file mode 100644
index 000000000..ac091faeb
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/b.expect.css
@@ -0,0 +1,5 @@
+.baz {
+ color: blue;
+}
+
+/*# sourceMappingURL=b.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map b/plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map
new file mode 100644
index 000000000..0a97ca290
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/b.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"b.css","sourcesContent":[".baz {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/concatenated.css b/plugins/postcss-cascade-layers/test/cli/out/concatenated.css
new file mode 100644
index 000000000..80726df77
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/concatenated.css
@@ -0,0 +1,6 @@
+.foo {
+ color: blue;
+}
+.baz {
+ color: blue;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css b/plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css
new file mode 100644
index 000000000..80726df77
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css
@@ -0,0 +1,6 @@
+.foo {
+ color: blue;
+}
+.baz {
+ color: blue;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/src/a.css b/plugins/postcss-cascade-layers/test/cli/src/a.css
new file mode 100644
index 000000000..cedf0a6d1
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/src/a.css
@@ -0,0 +1,3 @@
+.foo {
+ color: red;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/src/b.css b/plugins/postcss-cascade-layers/test/cli/src/b.css
new file mode 100644
index 000000000..5f2435156
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/src/b.css
@@ -0,0 +1,3 @@
+.baz {
+ color: red;
+}
diff --git a/plugins/postcss-cascade-layers/test/cli/test.sh b/plugins/postcss-cascade-layers/test/cli/test.sh
new file mode 100644
index 000000000..3cb230406
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/cli/test.sh
@@ -0,0 +1,94 @@
+set -e
+
+# Zero out result file
+echo '' > ./test/cli/basic.result.css;
+
+# Test with long flag
+postcss-base-plugin ./test/cli/basic.css --output ./test/cli/basic.result.css
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/basic.expect.css ./test/cli/basic.result.css
+
+# Reset result file
+cat ./test/cli/basic.css > ./test/cli/basic.replace.css;
+
+# Test replace
+postcss-base-plugin ./test/cli/basic.replace.css -r
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/basic.replace.css ./test/cli/basic.replace.expect.css
+
+# Zero out result file
+echo '' > ./test/cli/basic.color.result.css;
+
+# Test with short flags and plugin option
+postcss-base-plugin ./test/cli/basic.css -o ./test/cli/basic.color.result.css -p '{ "color": "purple" }'
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/basic.color.expect.css ./test/cli/basic.color.result.css
+
+# Zero out result file
+echo '' > ./test/cli/basic.stdin.result.css;
+
+# Test with stdin
+cat ./test/cli/basic.css | postcss-base-plugin > ./test/cli/basic.stdin.result.css
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/basic.stdin.expect.css ./test/cli/basic.stdin.result.css
+
+# Zero out result file
+echo '' > ./test/cli/basic.no-map.result.css;
+
+# Test source maps
+postcss-base-plugin ./test/cli/basic.css --no-map -o ./test/cli/basic.no-map.result.css
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/basic.no-map.expect.css ./test/cli/basic.no-map.result.css
+
+# Zero out result file
+echo '' > ./test/cli/basic.external-map.result.css;
+echo '' > ./test/cli/basic.external-map.result.css.map;
+
+# Test source maps
+cat ./test/cli/basic.css | postcss-base-plugin --map -o ./test/cli/basic.external-map.result.css
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/basic.external-map.expect.css ./test/cli/basic.external-map.result.css
+git --no-pager diff --no-index --word-diff ./test/cli/basic.external-map.expect.css.map ./test/cli/basic.external-map.result.css.map
+
+# Zero out result file
+echo '' > ./test/cli/out/a.css
+echo '' > ./test/cli/out/a.css.map
+echo '' > ./test/cli/out/b.css
+echo '' > ./test/cli/out/b.css.map
+
+# Test source maps
+postcss-base-plugin ./test/cli/src/a.css ./test/cli/src/b.css -m -d ./test/cli/out/
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/out/a.css ./test/cli/out/a.expect.css
+git --no-pager diff --no-index --word-diff ./test/cli/out/b.css ./test/cli/out/b.expect.css
+git --no-pager diff --no-index --word-diff ./test/cli/out/a.css.map ./test/cli/out/a.expect.css.map
+git --no-pager diff --no-index --word-diff ./test/cli/out/b.css.map ./test/cli/out/b.expect.css.map
+
+
+# Zero out result file
+echo '' > ./test/cli/out/concatenated.css
+
+# Test concat
+postcss-base-plugin ./test/cli/src/a.css ./test/cli/src/b.css > ./test/cli/out/concatenated.css
+
+# Check result
+git --no-pager diff --no-index --word-diff ./test/cli/out/concatenated.css ./test/cli/out/concatenated.expect.css
+
+# Dump some content
+echo 'foo' > ./test/cli/basic.failure.result.css
+
+# Test with incorrect arugments
+if postcss-base-plugin ./test/cli/basic.css --does-not-exist > ./test/cli/basic.failure.result.css; then
+ echo 'Test should have failed';
+ exit 1;
+else
+ # Check result
+ git --no-pager diff --no-index --word-diff ./test/cli/basic.failure.result.css ./test/cli/basic.failure.expect.css
+fi
diff --git a/plugins/postcss-cascade-layers/test/example.css b/plugins/postcss-cascade-layers/test/example.css
new file mode 100644
index 000000000..181f83a54
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/example.css
@@ -0,0 +1,7 @@
+.foo {
+ color: red;
+}
+
+.baz {
+ color: green;
+}
diff --git a/plugins/postcss-cascade-layers/test/example.expect.css b/plugins/postcss-cascade-layers/test/example.expect.css
new file mode 100644
index 000000000..9d738d5ac
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/example.expect.css
@@ -0,0 +1,7 @@
+.foo {
+ color: blue;
+}
+
+.baz {
+ color: green;
+}
diff --git a/plugins/postcss-cascade-layers/tsconfig.json b/plugins/postcss-cascade-layers/tsconfig.json
new file mode 100644
index 000000000..68a2606f6
--- /dev/null
+++ b/plugins/postcss-cascade-layers/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "declarationDir": "."
+ },
+ "include": ["./src/**/*"],
+ "exclude": ["dist"],
+}
From f46bc470e8d9b5829ffee5022c0aed3f9489fe74 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Fri, 11 Feb 2022 13:57:43 +0000
Subject: [PATCH 02/33] tidying up
---
plugins/postcss-cascade-layers/package.json | 4 -
plugins/postcss-cascade-layers/src/cli.ts | 16 ----
plugins/postcss-cascade-layers/src/index.ts | 3 +-
.../postcss-cascade-layers/test/_import.mjs | 2 +-
.../postcss-cascade-layers/test/_require.cjs | 2 +-
.../test/cli/basic.color.expect.css | 5 -
.../postcss-cascade-layers/test/cli/basic.css | 3 -
.../test/cli/basic.expect.css | 5 -
.../test/cli/basic.external-map.expect.css | 5 -
.../cli/basic.external-map.expect.css.map | 1 -
.../test/cli/basic.failure.expect.css | 0
.../test/cli/basic.no-map.expect.css | 3 -
.../test/cli/basic.replace.css | 5 -
.../test/cli/basic.replace.expect.css | 5 -
.../test/cli/basic.stdin.expect.css | 5 -
.../postcss-cascade-layers/test/cli/out/a.css | 5 -
.../test/cli/out/a.css.map | 1 -
.../test/cli/out/a.expect.css | 5 -
.../test/cli/out/a.expect.css.map | 1 -
.../postcss-cascade-layers/test/cli/out/b.css | 5 -
.../test/cli/out/b.css.map | 1 -
.../test/cli/out/b.expect.css | 5 -
.../test/cli/out/b.expect.css.map | 1 -
.../test/cli/out/concatenated.css | 6 --
.../test/cli/out/concatenated.expect.css | 6 --
.../postcss-cascade-layers/test/cli/src/a.css | 3 -
.../postcss-cascade-layers/test/cli/src/b.css | 3 -
.../postcss-cascade-layers/test/cli/test.sh | 94 -------------------
28 files changed, 3 insertions(+), 197 deletions(-)
delete mode 100644 plugins/postcss-cascade-layers/src/cli.ts
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.color.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.failure.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.replace.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.css.map
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.css.map
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/concatenated.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/src/a.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/src/b.css
delete mode 100644 plugins/postcss-cascade-layers/test/cli/test.sh
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
index 12d3de10d..e0a9e1f59 100644
--- a/plugins/postcss-cascade-layers/package.json
+++ b/plugins/postcss-cascade-layers/package.json
@@ -24,9 +24,6 @@
"README.md",
"dist"
],
- "bin": {
- "postcss-base-plugin": "dist/cli.cjs"
- },
"scripts": {
"build": "rollup -c ../../rollup/default.js",
"clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"",
@@ -35,7 +32,6 @@
"stryker": "stryker run --logLevel error",
"test": "node .tape.mjs && npm run test:exports",
"test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs",
- "test:cli": "bash ./test/cli/test.sh",
"test:exports": "node ./test/_import.mjs && node ./test/_require.cjs"
},
"peerDependencies": {
diff --git a/plugins/postcss-cascade-layers/src/cli.ts b/plugins/postcss-cascade-layers/src/cli.ts
deleted file mode 100644
index f087923cf..000000000
--- a/plugins/postcss-cascade-layers/src/cli.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import plugin from './index';
-import { cli, helpTextLogger } from '@csstools/base-cli';
-
-cli(
- plugin,
- ['color', 'another_option'],
- helpTextLogger(
- 'postcss-cascade-layers',
- 'Base Plugin',
- 'An example plugin CLI',
- {
- color: 'A CSS color',
- another_option: true,
- },
- ),
-);
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index ee1f96bc0..08c6fb48d 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -2,7 +2,7 @@ import type { PluginCreator } from 'postcss';
const creator: PluginCreator<{ color: string }> = (opts?: { color: string }) => {
return {
- postcssPlugin: 'postcss-base-plugin',
+ postcssPlugin: 'postcss-cascade-layers',
Declaration(decl) {
if (decl.value === 'red') {
if (opts && opts.color) {
@@ -18,4 +18,3 @@ const creator: PluginCreator<{ color: string }> = (opts?: { color: string }) =>
creator.postcss = true;
export default creator;
-
diff --git a/plugins/postcss-cascade-layers/test/_import.mjs b/plugins/postcss-cascade-layers/test/_import.mjs
index 0e4c84832..34ff16211 100644
--- a/plugins/postcss-cascade-layers/test/_import.mjs
+++ b/plugins/postcss-cascade-layers/test/_import.mjs
@@ -1,5 +1,5 @@
import assert from 'assert';
-import plugin from '@csstools/postcss-base-plugin';
+import plugin from '@csstools/postcss-cascade-layers';
plugin();
assert.ok(plugin.postcss, 'should have "postcss flag"');
diff --git a/plugins/postcss-cascade-layers/test/_require.cjs b/plugins/postcss-cascade-layers/test/_require.cjs
index 8b21a7929..0567d76bf 100644
--- a/plugins/postcss-cascade-layers/test/_require.cjs
+++ b/plugins/postcss-cascade-layers/test/_require.cjs
@@ -1,5 +1,5 @@
const assert = require('assert');
-const plugin = require('@csstools/postcss-base-plugin');
+const plugin = require('@csstools/postcss-cascade-layers');
plugin();
assert.ok(plugin.postcss, 'should have "postcss flag"');
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.color.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.color.expect.css
deleted file mode 100644
index 93f6b5280..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.color.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: purple;
-}
-
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtDQUNDLGFBQVU7QUFDWCIsImZpbGUiOiJiYXNpYy5jb2xvci5yZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.css b/plugins/postcss-cascade-layers/test/cli/basic.css
deleted file mode 100644
index cedf0a6d1..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.foo {
- color: red;
-}
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.expect.css
deleted file mode 100644
index 3f6f0d3a4..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtDQUNDLFdBQVU7QUFDWCIsImZpbGUiOiJiYXNpYy5yZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css
deleted file mode 100644
index b8aaa92b1..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=basic.external-map.result.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map b/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map
deleted file mode 100644
index 8f8e733cc..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.external-map.expect.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["../../stdin"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"basic.external-map.result.css","sourcesContent":[".foo {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.failure.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.failure.expect.css
deleted file mode 100644
index e69de29bb..000000000
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css
deleted file mode 100644
index c47fa33b8..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.no-map.expect.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.foo {
- color: blue;
-}
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.replace.css b/plugins/postcss-cascade-layers/test/cli/basic.replace.css
deleted file mode 100644
index 49bc11102..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.replace.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLnJlcGxhY2UuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0NBQ0MsV0FBVTtBQUNYIiwiZmlsZSI6ImJhc2ljLnJlcGxhY2UuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css
deleted file mode 100644
index 49bc11102..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.replace.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJhc2ljLnJlcGxhY2UuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0NBQ0MsV0FBVTtBQUNYIiwiZmlsZSI6ImJhc2ljLnJlcGxhY2UuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmZvbyB7XG5cdGNvbG9yOiByZWQ7XG59XG4iXX0= */
diff --git a/plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css b/plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css
deleted file mode 100644
index 7def14035..000000000
--- a/plugins/postcss-cascade-layers/test/cli/basic.stdin.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0NBQ0MsV0FBVTtBQUNYIiwiZmlsZSI6InN0ZG91dCIsInNvdXJjZXNDb250ZW50IjpbIi5mb28ge1xuXHRjb2xvcjogcmVkO1xufVxuIl19 */
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.css b/plugins/postcss-cascade-layers/test/cli/out/a.css
deleted file mode 100644
index 60e2001c0..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/a.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=a.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.css.map b/plugins/postcss-cascade-layers/test/cli/out/a.css.map
deleted file mode 100644
index d5c8b6f40..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/a.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["../src/a.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"a.css","sourcesContent":[".foo {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.expect.css b/plugins/postcss-cascade-layers/test/cli/out/a.expect.css
deleted file mode 100644
index 60e2001c0..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/a.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.foo {
- color: blue;
-}
-
-/*# sourceMappingURL=a.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map b/plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map
deleted file mode 100644
index d5c8b6f40..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/a.expect.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["../src/a.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"a.css","sourcesContent":[".foo {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.css b/plugins/postcss-cascade-layers/test/cli/out/b.css
deleted file mode 100644
index ac091faeb..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/b.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.baz {
- color: blue;
-}
-
-/*# sourceMappingURL=b.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.css.map b/plugins/postcss-cascade-layers/test/cli/out/b.css.map
deleted file mode 100644
index 0a97ca290..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/b.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["../src/b.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"b.css","sourcesContent":[".baz {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.expect.css b/plugins/postcss-cascade-layers/test/cli/out/b.expect.css
deleted file mode 100644
index ac091faeb..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/b.expect.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.baz {
- color: blue;
-}
-
-/*# sourceMappingURL=b.css.map */
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map b/plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map
deleted file mode 100644
index 0a97ca290..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/b.expect.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["../src/b.css"],"names":[],"mappings":"AAAA;CACC,WAAU;AACX","file":"b.css","sourcesContent":[".baz {\n\tcolor: red;\n}\n"]}
\ No newline at end of file
diff --git a/plugins/postcss-cascade-layers/test/cli/out/concatenated.css b/plugins/postcss-cascade-layers/test/cli/out/concatenated.css
deleted file mode 100644
index 80726df77..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/concatenated.css
+++ /dev/null
@@ -1,6 +0,0 @@
-.foo {
- color: blue;
-}
-.baz {
- color: blue;
-}
diff --git a/plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css b/plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css
deleted file mode 100644
index 80726df77..000000000
--- a/plugins/postcss-cascade-layers/test/cli/out/concatenated.expect.css
+++ /dev/null
@@ -1,6 +0,0 @@
-.foo {
- color: blue;
-}
-.baz {
- color: blue;
-}
diff --git a/plugins/postcss-cascade-layers/test/cli/src/a.css b/plugins/postcss-cascade-layers/test/cli/src/a.css
deleted file mode 100644
index cedf0a6d1..000000000
--- a/plugins/postcss-cascade-layers/test/cli/src/a.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.foo {
- color: red;
-}
diff --git a/plugins/postcss-cascade-layers/test/cli/src/b.css b/plugins/postcss-cascade-layers/test/cli/src/b.css
deleted file mode 100644
index 5f2435156..000000000
--- a/plugins/postcss-cascade-layers/test/cli/src/b.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.baz {
- color: red;
-}
diff --git a/plugins/postcss-cascade-layers/test/cli/test.sh b/plugins/postcss-cascade-layers/test/cli/test.sh
deleted file mode 100644
index 3cb230406..000000000
--- a/plugins/postcss-cascade-layers/test/cli/test.sh
+++ /dev/null
@@ -1,94 +0,0 @@
-set -e
-
-# Zero out result file
-echo '' > ./test/cli/basic.result.css;
-
-# Test with long flag
-postcss-base-plugin ./test/cli/basic.css --output ./test/cli/basic.result.css
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/basic.expect.css ./test/cli/basic.result.css
-
-# Reset result file
-cat ./test/cli/basic.css > ./test/cli/basic.replace.css;
-
-# Test replace
-postcss-base-plugin ./test/cli/basic.replace.css -r
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/basic.replace.css ./test/cli/basic.replace.expect.css
-
-# Zero out result file
-echo '' > ./test/cli/basic.color.result.css;
-
-# Test with short flags and plugin option
-postcss-base-plugin ./test/cli/basic.css -o ./test/cli/basic.color.result.css -p '{ "color": "purple" }'
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/basic.color.expect.css ./test/cli/basic.color.result.css
-
-# Zero out result file
-echo '' > ./test/cli/basic.stdin.result.css;
-
-# Test with stdin
-cat ./test/cli/basic.css | postcss-base-plugin > ./test/cli/basic.stdin.result.css
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/basic.stdin.expect.css ./test/cli/basic.stdin.result.css
-
-# Zero out result file
-echo '' > ./test/cli/basic.no-map.result.css;
-
-# Test source maps
-postcss-base-plugin ./test/cli/basic.css --no-map -o ./test/cli/basic.no-map.result.css
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/basic.no-map.expect.css ./test/cli/basic.no-map.result.css
-
-# Zero out result file
-echo '' > ./test/cli/basic.external-map.result.css;
-echo '' > ./test/cli/basic.external-map.result.css.map;
-
-# Test source maps
-cat ./test/cli/basic.css | postcss-base-plugin --map -o ./test/cli/basic.external-map.result.css
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/basic.external-map.expect.css ./test/cli/basic.external-map.result.css
-git --no-pager diff --no-index --word-diff ./test/cli/basic.external-map.expect.css.map ./test/cli/basic.external-map.result.css.map
-
-# Zero out result file
-echo '' > ./test/cli/out/a.css
-echo '' > ./test/cli/out/a.css.map
-echo '' > ./test/cli/out/b.css
-echo '' > ./test/cli/out/b.css.map
-
-# Test source maps
-postcss-base-plugin ./test/cli/src/a.css ./test/cli/src/b.css -m -d ./test/cli/out/
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/out/a.css ./test/cli/out/a.expect.css
-git --no-pager diff --no-index --word-diff ./test/cli/out/b.css ./test/cli/out/b.expect.css
-git --no-pager diff --no-index --word-diff ./test/cli/out/a.css.map ./test/cli/out/a.expect.css.map
-git --no-pager diff --no-index --word-diff ./test/cli/out/b.css.map ./test/cli/out/b.expect.css.map
-
-
-# Zero out result file
-echo '' > ./test/cli/out/concatenated.css
-
-# Test concat
-postcss-base-plugin ./test/cli/src/a.css ./test/cli/src/b.css > ./test/cli/out/concatenated.css
-
-# Check result
-git --no-pager diff --no-index --word-diff ./test/cli/out/concatenated.css ./test/cli/out/concatenated.expect.css
-
-# Dump some content
-echo 'foo' > ./test/cli/basic.failure.result.css
-
-# Test with incorrect arugments
-if postcss-base-plugin ./test/cli/basic.css --does-not-exist > ./test/cli/basic.failure.result.css; then
- echo 'Test should have failed';
- exit 1;
-else
- # Check result
- git --no-pager diff --no-index --word-diff ./test/cli/basic.failure.result.css ./test/cli/basic.failure.expect.css
-fi
From 233b186cbb63fc7c89b4fa6def804935a1c90a38 Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Wed, 23 Feb 2022 08:55:05 +0100
Subject: [PATCH 03/33] cascade layers tests
---
plugins/postcss-cascade-layers/package.json | 1 +
plugins/postcss-cascade-layers/src/index.ts | 8 +-
.../postcss-cascade-layers/stryker.conf.json | 19 -
.../postcss-cascade-layers/test/_browser.mjs | 101 ++++
plugins/postcss-cascade-layers/test/basic.css | 16 +-
.../test/basic.expect.css | 10 +-
.../test/wpt/layer-basic.html | 548 ++++++++++++++++++
.../wpt/layer-counter-style-override.html | 180 ++++++
8 files changed, 850 insertions(+), 33 deletions(-)
delete mode 100644 plugins/postcss-cascade-layers/stryker.conf.json
create mode 100644 plugins/postcss-cascade-layers/test/_browser.mjs
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-basic.html
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-counter-style-override.html
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
index e0a9e1f59..f315debce 100644
--- a/plugins/postcss-cascade-layers/package.json
+++ b/plugins/postcss-cascade-layers/package.json
@@ -31,6 +31,7 @@
"prepublishOnly": "npm run clean && npm run build && npm run test",
"stryker": "stryker run --logLevel error",
"test": "node .tape.mjs && npm run test:exports",
+ "test:browser": "node ./test/_browser.mjs",
"test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs",
"test:exports": "node ./test/_import.mjs && node ./test/_require.cjs"
},
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 08c6fb48d..3a3b51669 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -4,13 +4,7 @@ const creator: PluginCreator<{ color: string }> = (opts?: { color: string }) =>
return {
postcssPlugin: 'postcss-cascade-layers',
Declaration(decl) {
- if (decl.value === 'red') {
- if (opts && opts.color) {
- decl.value = opts.color;
- } else {
- decl.value = 'blue';
- }
- }
+ decl.remove();
},
};
};
diff --git a/plugins/postcss-cascade-layers/stryker.conf.json b/plugins/postcss-cascade-layers/stryker.conf.json
deleted file mode 100644
index 091dc740c..000000000
--- a/plugins/postcss-cascade-layers/stryker.conf.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "$schema": "../../node_modules/@stryker-mutator/core/schema/stryker-schema.json",
- "mutate": [
- "src/**/*.ts"
- ],
- "buildCommand": "npm run build",
- "testRunner": "command",
- "coverageAnalysis": "perTest",
- "tempDirName": "../../.stryker-tmp",
- "commandRunner": {
- "command": "npm run test:tape"
- },
- "thresholds": {
- "high": 100,
- "low": 100,
- "break": 100
- },
- "inPlace": true
-}
diff --git a/plugins/postcss-cascade-layers/test/_browser.mjs b/plugins/postcss-cascade-layers/test/_browser.mjs
new file mode 100644
index 000000000..fd81693ad
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/_browser.mjs
@@ -0,0 +1,101 @@
+import puppeteer from 'puppeteer';
+import http from 'http';
+import { promises as fsp } from 'fs';
+import plugin from '@csstools/postcss-cascade-layers';
+import postcss from 'postcss';
+
+// TODO : bring over more tests from WPT
+
+(async () => {
+ const requestListener = async function (req, res) {
+
+ const parsedUrl = new URL(req.url, 'http://localhost:8080');
+ const pathname = parsedUrl.pathname;
+
+ switch (pathname) {
+ case '/wpt/layer-basic.html':
+ res.setHeader('Content-type', 'text/html');
+ res.writeHead(200);
+ res.end(await fsp.readFile('test/wpt/layer-basic.html', 'utf8'));
+ break;
+ case '/wpt/layer-counter-style-override.html':
+ res.setHeader('Content-type', 'text/html');
+ res.writeHead(200);
+ res.end(await fsp.readFile('test/wpt/layer-counter-style-override.html', 'utf8'));
+ break;
+ case '/test/styles.css':
+ if (req.method === 'POST') {
+ const data = await new Promise((resolve, reject) => {
+ let buf = [];
+ req.on('data', (chunk) => {
+ buf.push(chunk);
+ });
+
+ req.on('end', () => {
+ resolve(Buffer.concat(buf).toString());
+ });
+
+ req.on('error', (err) => {
+ reject(err);
+ });
+ });
+
+ const css = await postcss([plugin]).process(data, { from: 'test/styles.css', to: 'test/styles.css' });
+ res.setHeader('Content-type', 'text/css');
+ res.writeHead(200);
+ res.end(css.css);
+ break;
+ }
+
+ // eslint-disable-next-line no-fallthrough
+ default:
+ res.setHeader('Content-type', 'text/plain' );
+ res.writeHead(404);
+ res.end('Not found');
+ break;
+ }
+ };
+
+ const server = http.createServer(requestListener);
+ server.listen(8080);
+
+ if (!process.env.DEBUG) {
+ const browser = await puppeteer.launch({
+ headless: true,
+ });
+
+ const page = await browser.newPage();
+ page.on('pageerror', (msg) => {
+ throw msg;
+ });
+
+ {
+ await page.goto('http://localhost:8080/wpt/layer-basic.html');
+ const result = await page.evaluate(async () => {
+ // eslint-disable-next-line no-undef
+ return await window.runTest();
+ });
+ if (!result) {
+ throw new Error('Test failed, expected "window.runTest()" to return true');
+ }
+ }
+
+ // TODO : uncomment
+ // {
+ // await page.goto('http://localhost:8080/wpt/layer-counter-style-override.html');
+ // const result = await page.evaluate(async () => {
+ // // eslint-disable-next-line no-undef
+ // return await window.runTest();
+ // });
+ // if (!result) {
+ // throw new Error('Test failed, expected "window.runTest()" to return true');
+ // }
+ // }
+
+ await browser.close();
+
+ await server.close();
+ } else {
+ console.log('visit : http://localhost:8080');
+ }
+})();
diff --git a/plugins/postcss-cascade-layers/test/basic.css b/plugins/postcss-cascade-layers/test/basic.css
index 181f83a54..f7ed69e5e 100644
--- a/plugins/postcss-cascade-layers/test/basic.css
+++ b/plugins/postcss-cascade-layers/test/basic.css
@@ -1,7 +1,15 @@
-.foo {
- color: red;
+@layer A {
+ target {
+ color: red;
+ }
}
-.baz {
- color: green;
+@layer {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.expect.css b/plugins/postcss-cascade-layers/test/basic.expect.css
index 9d738d5ac..5a19d28b9 100644
--- a/plugins/postcss-cascade-layers/test/basic.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.expect.css
@@ -1,7 +1,11 @@
-.foo {
- color: blue;
+target {
+ color: red;
}
-.baz {
+target:not(#something) {
color: green;
}
+
+target:not(#something#something) {
+ color: purple;
+}
diff --git a/plugins/postcss-cascade-layers/test/wpt/layer-basic.html b/plugins/postcss-cascade-layers/test/wpt/layer-basic.html
new file mode 100644
index 000000000..791e6872b
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/wpt/layer-basic.html
@@ -0,0 +1,548 @@
+
+
+
+
+ CSS Cascade Layers: Basic functionality
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/postcss-cascade-layers/test/wpt/layer-counter-style-override.html b/plugins/postcss-cascade-layers/test/wpt/layer-counter-style-override.html
new file mode 100644
index 000000000..0a67d1e6d
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/wpt/layer-counter-style-override.html
@@ -0,0 +1,180 @@
+
+Resolving @counter-style name conflicts with cascade layers
+
+
+
+
+
+
+
+
+
+
From 0d4e0010d07d3fd3e738c1126c23dc5fb45feb82 Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Wed, 23 Feb 2022 09:03:05 +0100
Subject: [PATCH 04/33] add test index
---
.../postcss-cascade-layers/test/_browser.mjs | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/plugins/postcss-cascade-layers/test/_browser.mjs b/plugins/postcss-cascade-layers/test/_browser.mjs
index fd81693ad..dc3a1c32c 100644
--- a/plugins/postcss-cascade-layers/test/_browser.mjs
+++ b/plugins/postcss-cascade-layers/test/_browser.mjs
@@ -13,6 +13,27 @@ import postcss from 'postcss';
const pathname = parsedUrl.pathname;
switch (pathname) {
+ case '':
+ case '/':
+ res.setHeader('Content-type', 'text/html');
+ res.writeHead(200);
+
+ // write html string with list of links to cases.
+ res.end(`
+
+
+ Cascade Layers Test
+
+
+ Cascade Layers Test
+
+
+
+ `);
+ break;
case '/wpt/layer-basic.html':
res.setHeader('Content-type', 'text/html');
res.writeHead(200);
From 98671bd5401e7881c786bfac9e9aa8213deb1092 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Fri, 25 Feb 2022 15:51:19 +0000
Subject: [PATCH 05/33] barebones start
---
package-lock.json | 30 +++++++++++++++
plugins/postcss-cascade-layers/src/index.ts | 35 ++++++++++++++----
plugins/postcss-color-rebeccapurple/.DS_Store | Bin 0 -> 6148 bytes
3 files changed, 58 insertions(+), 7 deletions(-)
create mode 100644 plugins/postcss-color-rebeccapurple/.DS_Store
diff --git a/package-lock.json b/package-lock.json
index b1cb41e66..20d011409 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -34,6 +34,7 @@
}
},
"cli/csstools-cli": {
+ "name": "@csstools/csstools-cli",
"version": "1.1.0",
"license": "CC0-1.0",
"dependencies": {
@@ -74,6 +75,7 @@
}
},
"experimental/css-has-pseudo": {
+ "name": "@csstools/css-has-pseudo-experimental",
"version": "0.2.1",
"license": "CC0-1.0",
"dependencies": {
@@ -1759,6 +1761,10 @@
"resolved": "plugins/postcss-base-plugin",
"link": true
},
+ "node_modules/@csstools/postcss-cascade-layers": {
+ "resolved": "plugins/postcss-cascade-layers",
+ "link": true
+ },
"node_modules/@csstools/postcss-color-function": {
"resolved": "plugins/postcss-color-function",
"link": true
@@ -6379,6 +6385,7 @@
}
},
"packages/base-cli": {
+ "name": "@csstools/base-cli",
"version": "0.1.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6389,6 +6396,7 @@
}
},
"packages/generate-test-cases": {
+ "name": "@csstools/generate-test-cases",
"version": "1.0.0",
"license": "CC0-1.0",
"devDependencies": {
@@ -6399,6 +6407,7 @@
}
},
"packages/postcss-tape": {
+ "name": "@csstools/postcss-tape",
"version": "1.0.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6516,6 +6525,7 @@
}
},
"plugins/postcss-base-plugin": {
+ "name": "@csstools/postcss-base-plugin",
"version": "0.0.0",
"license": "CC0-1.0",
"bin": {
@@ -6528,7 +6538,18 @@
"postcss": "^8.3"
}
},
+ "plugins/postcss-cascade-layers": {
+ "version": "0.0.0",
+ "license": "CC0-1.0",
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ }
+ },
"plugins/postcss-color-function": {
+ "name": "@csstools/postcss-color-function",
"version": "1.0.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6698,6 +6719,7 @@
}
},
"plugins/postcss-font-format-keywords": {
+ "name": "@csstools/postcss-font-format-keywords",
"version": "1.0.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6725,6 +6747,7 @@
}
},
"plugins/postcss-hwb-function": {
+ "name": "@csstools/postcss-hwb-function",
"version": "1.0.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6751,6 +6774,7 @@
}
},
"plugins/postcss-is-pseudo-class": {
+ "name": "@csstools/postcss-is-pseudo-class",
"version": "2.0.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6811,6 +6835,7 @@
}
},
"plugins/postcss-normalize-display-values": {
+ "name": "@csstools/postcss-normalize-display-values",
"version": "1.0.0",
"license": "CC0-1.0",
"dependencies": {
@@ -6855,6 +6880,7 @@
}
},
"plugins/postcss-progressive-custom-properties": {
+ "name": "@csstools/postcss-progressive-custom-properties",
"version": "1.0.0",
"license": "CC0-1.0",
"engines": {
@@ -8075,6 +8101,10 @@
"version": "file:plugins/postcss-base-plugin",
"requires": {}
},
+ "@csstools/postcss-cascade-layers": {
+ "version": "file:plugins/postcss-cascade-layers",
+ "requires": {}
+ },
"@csstools/postcss-color-function": {
"version": "file:plugins/postcss-color-function",
"requires": {
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 3a3b51669..4bbe2b396 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,14 +1,35 @@
-import type { PluginCreator } from 'postcss';
+import type { Container } from 'postcss';
-const creator: PluginCreator<{ color: string }> = (opts?: { color: string }) => {
+function postcssCascadeLayers(opts) {
return {
postcssPlugin: 'postcss-cascade-layers',
- Declaration(decl) {
- decl.remove();
+ Once(root: Container) {
+ root.walkAtRules((atRule) => {
+ if (atRule.name !== 'layer') {
+ return;
+ }
+
+ if (atRule.nodes && atRule.nodes.length) {
+ console.log(atRule.name, 'layer name', atRule.params);
+ // parse .params as layer name
+ // replace layer.name with :is()
+ // add layer.param after
+ // duplicate node contents
+
+ console.log('nodes', atRule.nodes);
+ } else {
+ console.log(atRule.name, atRule.params);
+ // parse .params as list of layer names
+ /*
+ pattern:
+ @layer foo foo.baz;
+ */
+ }
+ });
},
};
-};
+}
-creator.postcss = true;
+postcssCascadeLayers.postcss = true;
-export default creator;
+export default postcssCascadeLayers;
diff --git a/plugins/postcss-color-rebeccapurple/.DS_Store b/plugins/postcss-color-rebeccapurple/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..5172429f264de2441865cb4700216d4256da9242
GIT binary patch
literal 6148
zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQEJ;E>dF^gR7ES*H$5cmnB-G%I%Z
zD|S`@Z2$T80!#olbXV*=%*>dt@PRwdU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82
zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm<
zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8
H{*=HsM+*^=
literal 0
HcmV?d00001
From 194881f824f3033dfde6f6dc6d8788c6c29d317f Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Tue, 8 Mar 2022 14:28:48 +0000
Subject: [PATCH 06/33] replace anon layers
---
plugins/postcss-cascade-layers/src/index.ts | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 4bbe2b396..9ccd41c41 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -9,12 +9,19 @@ function postcssCascadeLayers(opts) {
return;
}
+ // if layer anon, name
+ if(atRule.params === '') {
+ atRule.params = ` anon${(Math.random()).toString()}`;
+ }
+
if (atRule.nodes && atRule.nodes.length) {
- console.log(atRule.name, 'layer name', atRule.params);
- // parse .params as layer name
- // replace layer.name with :is()
- // add layer.param after
- // duplicate node contents
+ const atRuleClone = atRule.clone();
+ atRuleClone.nodes.forEach((node) => {
+ const modifiedSelectors = node.selectors.map((selector) => {
+ return `${selector}:not()`;
+ });
+ atRule.parent.insertBefore(atRule, node.clone({ selectors: modifiedSelectors }));
+ });
console.log('nodes', atRule.nodes);
} else {
From 1dddaa54141cfe7b33372456fde399e9c6549cd2 Mon Sep 17 00:00:00 2001
From: Sana Javed
Date: Mon, 14 Mar 2022 09:36:15 -0500
Subject: [PATCH 07/33] First walkthrough, initializing data model
---
package-lock.json | 8 ++-
plugins/postcss-cascade-layers/.gitignore | 2 +
plugins/postcss-cascade-layers/package.json | 3 ++
plugins/postcss-cascade-layers/src/index.ts | 51 +++++++++----------
.../test/anon.layer.css | 27 ++++++++++
.../test/anon.layer.expect.css | 19 +++++++
6 files changed, 82 insertions(+), 28 deletions(-)
create mode 100644 plugins/postcss-cascade-layers/test/anon.layer.css
create mode 100644 plugins/postcss-cascade-layers/test/anon.layer.expect.css
diff --git a/package-lock.json b/package-lock.json
index 20d011409..fab154a6e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6539,8 +6539,12 @@
}
},
"plugins/postcss-cascade-layers": {
+ "name": "@csstools/postcss-cascade-layers",
"version": "0.0.0",
"license": "CC0-1.0",
+ "devDependencies": {
+ "postcss-tape": "^6.0.1"
+ },
"engines": {
"node": "^12 || ^14 || >=16"
},
@@ -8103,7 +8107,9 @@
},
"@csstools/postcss-cascade-layers": {
"version": "file:plugins/postcss-cascade-layers",
- "requires": {}
+ "requires": {
+ "postcss-tape": "^6.0.1"
+ }
},
"@csstools/postcss-color-function": {
"version": "file:plugins/postcss-color-function",
diff --git a/plugins/postcss-cascade-layers/.gitignore b/plugins/postcss-cascade-layers/.gitignore
index 7172b04f1..3336e93ae 100644
--- a/plugins/postcss-cascade-layers/.gitignore
+++ b/plugins/postcss-cascade-layers/.gitignore
@@ -1,6 +1,8 @@
node_modules
+dist
package-lock.json
yarn.lock
+*.log*
*.result.css
*.result.css.map
dist/*
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
index f315debce..2731fd062 100644
--- a/plugins/postcss-cascade-layers/package.json
+++ b/plugins/postcss-cascade-layers/package.json
@@ -48,5 +48,8 @@
},
"volta": {
"extends": "../../package.json"
+ },
+ "devDependencies": {
+ "postcss-tape": "^6.0.1"
}
}
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 9ccd41c41..becc724bc 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,38 +1,35 @@
-import type { Container } from 'postcss';
+import { Container } from "postcss";
function postcssCascadeLayers(opts) {
return {
- postcssPlugin: 'postcss-cascade-layers',
+ postcssPlugin: "postcss-cascade-layers",
Once(root: Container) {
- root.walkAtRules((atRule) => {
- if (atRule.name !== 'layer') {
- return;
- }
+ let layerCount = 0;
+ let layerOrder = {};
- // if layer anon, name
- if(atRule.params === '') {
- atRule.params = ` anon${(Math.random()).toString()}`;
+ // 1st walkthrough to rename anon layers and store state (no modification of layer styles)
+ root.walkAtRules("layer", (atRule) => {
+ // give anonymous layers a name
+ if (!atRule.params) {
+ atRule.params = `anon${layerCount}`;
}
- if (atRule.nodes && atRule.nodes.length) {
- const atRuleClone = atRule.clone();
- atRuleClone.nodes.forEach((node) => {
- const modifiedSelectors = node.selectors.map((selector) => {
- return `${selector}:not()`;
- });
- atRule.parent.insertBefore(atRule, node.clone({ selectors: modifiedSelectors }));
- });
-
- console.log('nodes', atRule.nodes);
- } else {
- console.log(atRule.name, atRule.params);
- // parse .params as list of layer names
- /*
- pattern:
- @layer foo foo.baz;
- */
- }
+ layerCount += 1;
+ layerOrder[atRule.params] = layerCount;
});
+
+ // 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount + 1)
+ // root.walkRules((rule) => {
+ // console.log(rule, "second walkthrough");
+ // });
+
+ // 3rd walkthrough to transform layered styles:
+ // - move out styles from atRule, insert before: https://postcss.org/api/#container-insertbefore
+ // - delete empty atRule
+ // - give selectors the specifity they need based on layerPriority state
+ // root.walkAtRules((atRule) => {
+ // console.log(atRule, "third walkthrough");
+ // });
},
};
}
diff --git a/plugins/postcss-cascade-layers/test/anon.layer.css b/plugins/postcss-cascade-layers/test/anon.layer.css
new file mode 100644
index 000000000..1cd1f743d
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/anon.layer.css
@@ -0,0 +1,27 @@
+@layer A {
+ target {
+ color: red;
+ }
+}
+
+@layer B {
+ target {
+ color: orange;
+ }
+}
+
+@layer C {
+ target {
+ color: yellow;
+ }
+}
+
+@layer {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
+}
diff --git a/plugins/postcss-cascade-layers/test/anon.layer.expect.css b/plugins/postcss-cascade-layers/test/anon.layer.expect.css
new file mode 100644
index 000000000..869955a7c
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/anon.layer.expect.css
@@ -0,0 +1,19 @@
+target:not(#something #something #something #something #something) {
+ color: purple;
+}
+
+target:not(#something #something #something #something) {
+ color: green;
+}
+
+target:not(#something #something #something) {
+ color: yellow;
+}
+
+target:not(#something #something) {
+ color: orange;
+}
+
+target:not(#something #something #something) {
+ color: red;
+}
From c35564366b7ec515e3484d3c7eac9e6e26af945f Mon Sep 17 00:00:00 2001
From: Sana Javed
Date: Tue, 22 Mar 2022 09:15:31 -0500
Subject: [PATCH 08/33] Handling nested layers (#4)
Handling layer nesting
---
plugins/postcss-cascade-layers/.gitignore | 1 -
plugins/postcss-cascade-layers/.tape.mjs | 3 ++
plugins/postcss-cascade-layers/src/index.ts | 40 ++++++++++++++++++-
.../postcss-cascade-layers/test/nested.css | 15 +++++++
.../test/nested.expect.css | 16 ++++++++
5 files changed, 72 insertions(+), 3 deletions(-)
create mode 100644 plugins/postcss-cascade-layers/test/nested.css
create mode 100644 plugins/postcss-cascade-layers/test/nested.expect.css
diff --git a/plugins/postcss-cascade-layers/.gitignore b/plugins/postcss-cascade-layers/.gitignore
index 3336e93ae..2d56f5208 100644
--- a/plugins/postcss-cascade-layers/.gitignore
+++ b/plugins/postcss-cascade-layers/.gitignore
@@ -5,4 +5,3 @@ yarn.lock
*.log*
*.result.css
*.result.css.map
-dist/*
diff --git a/plugins/postcss-cascade-layers/.tape.mjs b/plugins/postcss-cascade-layers/.tape.mjs
index d75456c1e..033e5e2cf 100644
--- a/plugins/postcss-cascade-layers/.tape.mjs
+++ b/plugins/postcss-cascade-layers/.tape.mjs
@@ -14,4 +14,7 @@ postcssTape(plugin)({
example: {
message: "minimal example",
},
+ nested: {
+ message: "supporting nested layer usage",
+ },
});
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index becc724bc..48c073b0c 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -14,13 +14,48 @@ function postcssCascadeLayers(opts) {
atRule.params = `anon${layerCount}`;
}
+ let hasNestedLayers = false;
+ let hasUnlayeredStyles = false;
+
+ // check for where a layer has nested layers AND styles outside of those layers
+ atRule.each((node) => {
+ if (node.type == "atrule") {
+ hasNestedLayers = true;
+ } else if (node.type == "rule") {
+ hasUnlayeredStyles = true;
+ }
+ });
+
+ if (hasNestedLayers && hasUnlayeredStyles) {
+ //create new final layer via cloning, empty it
+ const implicitLayer = atRule.clone({
+ params: `${atRule.params}-implicit`,
+ });
+ implicitLayer.each((node) => {
+ node.remove();
+ });
+
+ // insert new layer
+ atRule.append(implicitLayer);
+
+ // go through the unlayered rules, clone, and delete from top level atRule
+ atRule.each((node) => {
+ if (node.type == "rule") {
+ implicitLayer.append(node.clone());
+ node.remove();
+ }
+ });
+ }
+ });
+
+ root.walkAtRules("layer", (layer) => {
layerCount += 1;
- layerOrder[atRule.params] = layerCount;
+ layerOrder[layer.params] = layerCount;
});
// 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount + 1)
// root.walkRules((rule) => {
- // console.log(rule, "second walkthrough");
+ // console.log("second walkthrough");
// });
// 3rd walkthrough to transform layered styles:
@@ -30,6 +65,7 @@ function postcssCascadeLayers(opts) {
// root.walkAtRules((atRule) => {
// console.log(atRule, "third walkthrough");
// });
+ console.log(layerOrder);
},
};
}
diff --git a/plugins/postcss-cascade-layers/test/nested.css b/plugins/postcss-cascade-layers/test/nested.css
new file mode 100644
index 000000000..5d66d89fc
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/nested.css
@@ -0,0 +1,15 @@
+@layer A {
+ target {
+ color: red;
+ }
+
+ p {
+ color: blue;
+ }
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }
+}
diff --git a/plugins/postcss-cascade-layers/test/nested.expect.css b/plugins/postcss-cascade-layers/test/nested.expect.css
new file mode 100644
index 000000000..ac93bbbe8
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/nested.expect.css
@@ -0,0 +1,16 @@
+@layer A {
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }@layer A-implicit {
+ target {
+ color: red;
+ }
+
+ p {
+ color: blue;
+ }
+}
+}
From 6589bdfa1d9e0a55f49477ec610d4b4418dd0a31 Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Tue, 22 Mar 2022 19:47:49 +0100
Subject: [PATCH 09/33] cascade layers : linting and a few fixes
---
package-lock.json | 7 +--
plugins/postcss-cascade-layers/package.json | 3 --
plugins/postcss-cascade-layers/src/index.ts | 26 ++++++-----
.../test/basic.color.expect.css | 16 +++++--
.../test/basic.expect.css | 14 +++---
.../test/example.expect.css | 2 +-
.../postcss-cascade-layers/test/nested.css | 37 ++++++++++++++++
.../test/nested.expect.css | 43 +++++++++++++++++++
8 files changed, 119 insertions(+), 29 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index fab154a6e..a5f8d6f34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6542,9 +6542,6 @@
"name": "@csstools/postcss-cascade-layers",
"version": "0.0.0",
"license": "CC0-1.0",
- "devDependencies": {
- "postcss-tape": "^6.0.1"
- },
"engines": {
"node": "^12 || ^14 || >=16"
},
@@ -8107,9 +8104,7 @@
},
"@csstools/postcss-cascade-layers": {
"version": "file:plugins/postcss-cascade-layers",
- "requires": {
- "postcss-tape": "^6.0.1"
- }
+ "requires": {}
},
"@csstools/postcss-color-function": {
"version": "file:plugins/postcss-color-function",
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
index 2731fd062..f315debce 100644
--- a/plugins/postcss-cascade-layers/package.json
+++ b/plugins/postcss-cascade-layers/package.json
@@ -48,8 +48,5 @@
},
"volta": {
"extends": "../../package.json"
- },
- "devDependencies": {
- "postcss-tape": "^6.0.1"
}
}
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 48c073b0c..b30388f9e 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,14 +1,14 @@
-import { Container } from "postcss";
+import { Container } from 'postcss';
function postcssCascadeLayers(opts) {
return {
- postcssPlugin: "postcss-cascade-layers",
+ postcssPlugin: 'postcss-cascade-layers',
Once(root: Container) {
let layerCount = 0;
- let layerOrder = {};
+ const layerOrder = {};
// 1st walkthrough to rename anon layers and store state (no modification of layer styles)
- root.walkAtRules("layer", (atRule) => {
+ root.walkAtRules('layer', (atRule) => {
// give anonymous layers a name
if (!atRule.params) {
atRule.params = `anon${layerCount}`;
@@ -19,11 +19,15 @@ function postcssCascadeLayers(opts) {
// check for where a layer has nested layers AND styles outside of those layers
atRule.each((node) => {
- if (node.type == "atrule") {
+ if (node.type == 'atrule' && node.name === 'layer') {
hasNestedLayers = true;
- } else if (node.type == "rule") {
+ } else {
hasUnlayeredStyles = true;
}
+
+ if (hasNestedLayers && hasUnlayeredStyles) {
+ return false;
+ }
});
if (hasNestedLayers && hasUnlayeredStyles) {
@@ -40,15 +44,17 @@ function postcssCascadeLayers(opts) {
// go through the unlayered rules, clone, and delete from top level atRule
atRule.each((node) => {
- if (node.type == "rule") {
- implicitLayer.append(node.clone());
- node.remove();
+ if (node.type == 'atrule' && node.name === 'layer') {
+ return;
}
+
+ implicitLayer.append(node.clone());
+ node.remove();
});
}
});
- root.walkAtRules("layer", (layer) => {
+ root.walkAtRules('layer', (layer) => {
layerCount += 1;
layerOrder[layer.params] = layerCount;
});
diff --git a/plugins/postcss-cascade-layers/test/basic.color.expect.css b/plugins/postcss-cascade-layers/test/basic.color.expect.css
index 041b1dabf..b8450fece 100644
--- a/plugins/postcss-cascade-layers/test/basic.color.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.color.expect.css
@@ -1,7 +1,15 @@
-.foo {
- color: purple;
+@layer A {
+ target {
+ color: red;
+ }
+}
+
+@layeranon0 {
+ target {
+ color: green;
+ }
}
-.baz {
- color: green;
+target {
+ color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.expect.css b/plugins/postcss-cascade-layers/test/basic.expect.css
index 5a19d28b9..b8450fece 100644
--- a/plugins/postcss-cascade-layers/test/basic.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.expect.css
@@ -1,11 +1,15 @@
-target {
- color: red;
+@layer A {
+ target {
+ color: red;
+ }
}
-target:not(#something) {
- color: green;
+@layeranon0 {
+ target {
+ color: green;
+ }
}
-target:not(#something#something) {
+target {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/example.expect.css b/plugins/postcss-cascade-layers/test/example.expect.css
index 9d738d5ac..181f83a54 100644
--- a/plugins/postcss-cascade-layers/test/example.expect.css
+++ b/plugins/postcss-cascade-layers/test/example.expect.css
@@ -1,5 +1,5 @@
.foo {
- color: blue;
+ color: red;
}
.baz {
diff --git a/plugins/postcss-cascade-layers/test/nested.css b/plugins/postcss-cascade-layers/test/nested.css
index 5d66d89fc..c9b05e915 100644
--- a/plugins/postcss-cascade-layers/test/nested.css
+++ b/plugins/postcss-cascade-layers/test/nested.css
@@ -12,4 +12,41 @@
color: yellow;
}
}
+
+ i {
+ color: red;
+ }
+}
+
+@layer B {
+ @keyframes slide-left {
+ from {
+ margin-left: 0;
+ }
+
+ to {
+ margin-left: -100%;
+ }
+ }
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }
+}
+
+@layer C {
+ @media (prefers-color-scheme: dark) {
+ h1 {
+ color: red;
+ background: black;
+ }
+ }
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }
}
diff --git a/plugins/postcss-cascade-layers/test/nested.expect.css b/plugins/postcss-cascade-layers/test/nested.expect.css
index ac93bbbe8..31b40d981 100644
--- a/plugins/postcss-cascade-layers/test/nested.expect.css
+++ b/plugins/postcss-cascade-layers/test/nested.expect.css
@@ -12,5 +12,48 @@
p {
color: blue;
}
+
+ i {
+ color: red;
+ }
+}
+}
+
+@layer B {
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }
+
+@layer B-implicit {
+ @keyframes slide-left {
+ from {
+ margin-left: 0;
+ }
+
+ to {
+ margin-left: -100%;
+ }
+ }
+}
+}
+
+@layer C {
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }
+
+@layer C-implicit {
+ @media (prefers-color-scheme: dark) {
+ h1 {
+ color: red;
+ background: black;
+ }
+ }
}
}
From abefae97d7d700a34c42dfa62a223047c35c97f4 Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Tue, 22 Mar 2022 20:00:44 +0100
Subject: [PATCH 10/33] more fixes
---
plugins/postcss-cascade-layers/.tape.mjs | 6 +++++
plugins/postcss-cascade-layers/src/index.ts | 1 +
.../test/{anon.layer.css => anon-layer.css} | 0
.../test/anon-layer.expect.css | 27 +++++++++++++++++++
.../test/anon.layer.expect.css | 19 -------------
.../test/basic.color.expect.css | 2 +-
.../test/basic.expect.css | 2 +-
7 files changed, 36 insertions(+), 21 deletions(-)
rename plugins/postcss-cascade-layers/test/{anon.layer.css => anon-layer.css} (100%)
create mode 100644 plugins/postcss-cascade-layers/test/anon-layer.expect.css
delete mode 100644 plugins/postcss-cascade-layers/test/anon.layer.expect.css
diff --git a/plugins/postcss-cascade-layers/.tape.mjs b/plugins/postcss-cascade-layers/.tape.mjs
index 033e5e2cf..67c6fa058 100644
--- a/plugins/postcss-cascade-layers/.tape.mjs
+++ b/plugins/postcss-cascade-layers/.tape.mjs
@@ -17,4 +17,10 @@ postcssTape(plugin)({
nested: {
message: "supporting nested layer usage",
},
+ 'anon-layer': {
+ message: "supporting anonymous layer usage",
+ },
+ nested: {
+ message: "supporting nested layer usage",
+ },
});
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index b30388f9e..557c543fb 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -11,6 +11,7 @@ function postcssCascadeLayers(opts) {
root.walkAtRules('layer', (atRule) => {
// give anonymous layers a name
if (!atRule.params) {
+ atRule.raws.afterName = ' ';
atRule.params = `anon${layerCount}`;
}
diff --git a/plugins/postcss-cascade-layers/test/anon.layer.css b/plugins/postcss-cascade-layers/test/anon-layer.css
similarity index 100%
rename from plugins/postcss-cascade-layers/test/anon.layer.css
rename to plugins/postcss-cascade-layers/test/anon-layer.css
diff --git a/plugins/postcss-cascade-layers/test/anon-layer.expect.css b/plugins/postcss-cascade-layers/test/anon-layer.expect.css
new file mode 100644
index 000000000..b30e8198c
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/anon-layer.expect.css
@@ -0,0 +1,27 @@
+@layer A {
+ target {
+ color: red;
+ }
+}
+
+@layer B {
+ target {
+ color: orange;
+ }
+}
+
+@layer C {
+ target {
+ color: yellow;
+ }
+}
+
+@layer anon0 {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
+}
diff --git a/plugins/postcss-cascade-layers/test/anon.layer.expect.css b/plugins/postcss-cascade-layers/test/anon.layer.expect.css
deleted file mode 100644
index 869955a7c..000000000
--- a/plugins/postcss-cascade-layers/test/anon.layer.expect.css
+++ /dev/null
@@ -1,19 +0,0 @@
-target:not(#something #something #something #something #something) {
- color: purple;
-}
-
-target:not(#something #something #something #something) {
- color: green;
-}
-
-target:not(#something #something #something) {
- color: yellow;
-}
-
-target:not(#something #something) {
- color: orange;
-}
-
-target:not(#something #something #something) {
- color: red;
-}
diff --git a/plugins/postcss-cascade-layers/test/basic.color.expect.css b/plugins/postcss-cascade-layers/test/basic.color.expect.css
index b8450fece..8c2d3172e 100644
--- a/plugins/postcss-cascade-layers/test/basic.color.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.color.expect.css
@@ -4,7 +4,7 @@
}
}
-@layeranon0 {
+@layer anon0 {
target {
color: green;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.expect.css b/plugins/postcss-cascade-layers/test/basic.expect.css
index b8450fece..8c2d3172e 100644
--- a/plugins/postcss-cascade-layers/test/basic.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.expect.css
@@ -4,7 +4,7 @@
}
}
-@layeranon0 {
+@layer anon0 {
target {
color: green;
}
From e1e6d5ec3c748ee3842ac21138591f5648b028c9 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Tue, 22 Mar 2022 19:45:24 +0000
Subject: [PATCH 11/33] adding unlayered styles to data model
---
plugins/postcss-cascade-layers/src/index.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 48c073b0c..b9818091f 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -54,9 +54,9 @@ function postcssCascadeLayers(opts) {
});
// 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount + 1)
- // root.walkRules((rule) => {
- // console.log("second walkthrough");
- // });
+ root.walkRules((rule) => {
+ layerOrder['unlayered ' + rule.selector] = layerCount + 1;
+ });
// 3rd walkthrough to transform layered styles:
// - move out styles from atRule, insert before: https://postcss.org/api/#container-insertbefore
From 31f73a8cbd07d5584ffadf28f73dc56de5d8f304 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Thu, 24 Mar 2022 16:28:14 +0000
Subject: [PATCH 12/33] add suggested code
---
plugins/postcss-cascade-layers/src/index.ts | 38 ++++++++++++++++++---
1 file changed, 33 insertions(+), 5 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index b9818091f..320f07313 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,4 +1,4 @@
-import { Container } from "postcss";
+import { Container, AtRule, Node} from "postcss";
function postcssCascadeLayers(opts) {
return {
@@ -53,10 +53,38 @@ function postcssCascadeLayers(opts) {
layerOrder[layer.params] = layerCount;
});
- // 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount + 1)
- root.walkRules((rule) => {
- layerOrder['unlayered ' + rule.selector] = layerCount + 1;
- });
+ // functions for 2nd walkthrough
+ function generateNot(specificity: number) {
+ let list = ''; for (let i = 0; i < specificity; i++) {
+ list += '#\\#'; // something short but still very uncommon
+ } return `:not(${list})`;
+ }
+ function hasLayerAtRuleAncestor(node: Node): boolean {
+ let parent = node.parent;
+ while (parent) {
+ if (parent.type !== 'atrule') {
+ parent = parent.parent; continue;
+ }
+ if ((parent as AtRule).name === 'layer') {
+ return true;
+ }
+ parent = parent.parent;
+ }
+ return false; }
+
+ if (!layerCount) {
+ // no layers, so nothing to transform.
+ return;
+ } // 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount)
+ root.walkRules((rule) => {
+ if (hasLayerAtRuleAncestor(rule)) {
+ return;
+ } rule.selectors = rule.selectors.map((selector) => {
+ // Needs `postcss-selector-parser` to insert `:not()` before any pseudo elements like `::after`
+ // This is a side track and can be fixed later.
+ return `${generateNot(layerCount)} ${selector}`;
+ });
+ });
// 3rd walkthrough to transform layered styles:
// - move out styles from atRule, insert before: https://postcss.org/api/#container-insertbefore
From cee8ce4c4f7b59fdcd4dbbd5a8d9410f77ae91f9 Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Sun, 27 Mar 2022 19:04:22 +0200
Subject: [PATCH 13/33] more tweaks
---
plugins/postcss-cascade-layers/.tape.mjs | 7 +-
plugins/postcss-cascade-layers/INSTALL.md | 177 +++++++++++++++++-
plugins/postcss-cascade-layers/README.md | 72 ++++++-
plugins/postcss-cascade-layers/docs/README.md | 34 ++++
plugins/postcss-cascade-layers/package.json | 108 ++++++-----
plugins/postcss-cascade-layers/src/index.ts | 10 +-
.../postcss-cascade-layers/test/example.css | 7 -
.../test/example.expect.css | 7 -
.../test/examples/example.css | 9 +
.../test/examples/example.expect.css | 9 +
10 files changed, 361 insertions(+), 79 deletions(-)
create mode 100644 plugins/postcss-cascade-layers/docs/README.md
delete mode 100644 plugins/postcss-cascade-layers/test/example.css
delete mode 100644 plugins/postcss-cascade-layers/test/example.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/examples/example.css
create mode 100644 plugins/postcss-cascade-layers/test/examples/example.expect.css
diff --git a/plugins/postcss-cascade-layers/.tape.mjs b/plugins/postcss-cascade-layers/.tape.mjs
index 67c6fa058..364936753 100644
--- a/plugins/postcss-cascade-layers/.tape.mjs
+++ b/plugins/postcss-cascade-layers/.tape.mjs
@@ -11,16 +11,13 @@ postcssTape(plugin)({
color: 'purple'
}
},
- example: {
- message: "minimal example",
- },
nested: {
message: "supporting nested layer usage",
},
'anon-layer': {
message: "supporting anonymous layer usage",
},
- nested: {
- message: "supporting nested layer usage",
+ 'examples/example': {
+ message: "minimal example",
},
});
diff --git a/plugins/postcss-cascade-layers/INSTALL.md b/plugins/postcss-cascade-layers/INSTALL.md
index ceacefa74..7ff70288f 100644
--- a/plugins/postcss-cascade-layers/INSTALL.md
+++ b/plugins/postcss-cascade-layers/INSTALL.md
@@ -1,3 +1,176 @@
-# Installing PostCSS Base Plugin
+# Installing PostCSS Cascade Layers
-
+[PostCSS Cascade Layers] runs in all Node environments, with special instructions for:
+
+| [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) |
+| --- | --- | --- | --- | --- | --- |
+
+## Node
+
+Add [PostCSS Cascade Layers] to your project:
+
+```bash
+npm install postcss @csstools/postcss-cascade-layers --save-dev
+```
+
+Use it as a [PostCSS] plugin:
+
+```js
+const postcss = require('postcss');
+const postcssCascadeLayers = require('@csstools/postcss-cascade-layers');
+
+postcss([
+ postcssCascadeLayers(/* pluginOptions */)
+]).process(YOUR_CSS /*, processOptions */);
+```
+
+## PostCSS CLI
+
+Add [PostCSS CLI] to your project:
+
+```bash
+npm install postcss-cli @csstools/postcss-cascade-layers --save-dev
+```
+
+Use [PostCSS Cascade Layers] in your `postcss.config.js` configuration file:
+
+```js
+const postcssCascadeLayers = require('@csstools/postcss-cascade-layers');
+
+module.exports = {
+ plugins: [
+ postcssCascadeLayers(/* pluginOptions */)
+ ]
+}
+```
+
+## Webpack
+
+_Webpack version 5_
+
+Add [PostCSS Loader] to your project:
+
+```bash
+npm install postcss-loader @csstools/postcss-cascade-layers --save-dev
+```
+
+Use [PostCSS Cascade Layers] 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-cascade-layers",
+ {
+ // 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-cascade-layers --save-dev
+```
+
+Use [React App Rewire PostCSS] and [PostCSS Cascade Layers] in your
+`config-overrides.js` file:
+
+```js
+const reactAppRewirePostcss = require('react-app-rewire-postcss');
+const postcssCascadeLayers = require('@csstools/postcss-cascade-layers');
+
+module.exports = config => reactAppRewirePostcss(config, {
+ plugins: () => [
+ postcssCascadeLayers(/* pluginOptions */)
+ ]
+});
+```
+
+## Gulp
+
+Add [Gulp PostCSS] to your project:
+
+```bash
+npm install gulp-postcss @csstools/postcss-cascade-layers --save-dev
+```
+
+Use [PostCSS Cascade Layers] in your Gulpfile:
+
+```js
+const postcss = require('gulp-postcss');
+const postcssCascadeLayers = require('@csstools/postcss-cascade-layers');
+
+gulp.task('css', function () {
+ var plugins = [
+ postcssCascadeLayers(/* 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-cascade-layers --save-dev
+```
+
+Use [PostCSS Cascade Layers] in your Gruntfile:
+
+```js
+const postcssCascadeLayers = require('@csstools/postcss-cascade-layers');
+
+grunt.loadNpmTasks('grunt-postcss');
+
+grunt.initConfig({
+ postcss: {
+ options: {
+ processors: [
+ postcssCascadeLayers(/* 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 Cascade Layers]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-cascade-layers
+[React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss
+[React App Rewired]: https://github.com/timarney/react-app-rewired
diff --git a/plugins/postcss-cascade-layers/README.md b/plugins/postcss-cascade-layers/README.md
index a631e79c7..cba698666 100644
--- a/plugins/postcss-cascade-layers/README.md
+++ b/plugins/postcss-cascade-layers/README.md
@@ -1,5 +1,71 @@
-# PostCSS Base Plugin [
][postcss]
+# PostCSS Cascade Layers [
][postcss]
-
+[
][npm-url]
+[
][css-url]
+[
][cli-url]
+[
][discord]
-[postcss]: https://github.com/postcss/postcss
+[PostCSS Cascade Layers] lets use `@layer` following the [Cascade Layers Specification].
+
+```pcss
+@layer {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
+}
+
+/* becomes */
+
+@layer anon0 {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
+}
+```
+
+## Usage
+
+Add [PostCSS Cascade Layers] to your project:
+
+```bash
+npm install postcss @csstools/postcss-cascade-layers --save-dev
+```
+
+Use it as a [PostCSS] plugin:
+
+```js
+const postcss = require('postcss');
+const postcssCascadeLayers = require('@csstools/postcss-cascade-layers');
+
+postcss([
+ postcssCascadeLayers(/* pluginOptions */)
+]).process(YOUR_CSS /*, processOptions */);
+```
+
+[PostCSS Cascade Layers] 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) |
+| --- | --- | --- | --- | --- | --- |
+
+
+
+[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
+[css-url]: https://cssdb.org/#cascade-layers
+[discord]: https://discord.gg/bUadyRwkJS
+[npm-url]: https://www.npmjs.com/package/@csstools/postcss-cascade-layers
+
+[Gulp PostCSS]: https://github.com/postcss/gulp-postcss
+[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss
+[PostCSS]: https://github.com/postcss/postcss
+[PostCSS Loader]: https://github.com/postcss/postcss-loader
+[PostCSS Cascade Layers]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-cascade-layers
+[Cascade Layers Specification]: https://www.w3.org/TR/css-cascade-5/#layering
diff --git a/plugins/postcss-cascade-layers/docs/README.md b/plugins/postcss-cascade-layers/docs/README.md
new file mode 100644
index 000000000..3325b8725
--- /dev/null
+++ b/plugins/postcss-cascade-layers/docs/README.md
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[] lets use `@layer` following the [Cascade Layers Specification].
+
+```pcss
+
+
+/* becomes */
+
+
+```
+
+
+
+
+
+
+
+
+[Cascade Layers Specification]:
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
index f315debce..4ab887aa2 100644
--- a/plugins/postcss-cascade-layers/package.json
+++ b/plugins/postcss-cascade-layers/package.json
@@ -1,52 +1,60 @@
{
- "name": "@csstools/postcss-cascade-layers",
- "private": true,
- "version": "0.0.0",
- "description": "A base plugin",
- "author": "Jonathan Neal ",
- "license": "CC0-1.0",
- "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"
- ],
- "scripts": {
- "build": "rollup -c ../../rollup/default.js",
- "clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"",
- "lint": "eslint ./src --ext .js --ext .ts --ext .mjs --no-error-on-unmatched-pattern",
- "prepublishOnly": "npm run clean && npm run build && npm run test",
- "stryker": "stryker run --logLevel error",
- "test": "node .tape.mjs && npm run test:exports",
- "test:browser": "node ./test/_browser.mjs",
- "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs",
- "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs"
- },
- "peerDependencies": {
- "postcss": "^8.3"
- },
- "keywords": [
- "postcss-plugin"
- ],
- "repository": {
- "type": "git",
- "url": "https://github.com/csstools/postcss-plugins.git",
- "directory": "plugins/postcss-cascade-layers"
- },
- "volta": {
- "extends": "../../package.json"
- }
+ "name": "@csstools/postcss-cascade-layers",
+ "description": "A base plugin",
+ "version": "0.0.0",
+ "author": "Jonathan Neal ",
+ "license": "CC0-1.0",
+ "private": true,
+ "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"
+ ],
+ "peerDependencies": {
+ "postcss": "^8.3"
+ },
+ "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.mjs && npm run test:exports",
+ "test:browser": "node ./test/_browser.mjs",
+ "test:exports": "node ./test/_import.mjs && node ./test/_require.cjs",
+ "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/csstools/postcss-plugins.git",
+ "directory": "plugins/postcss-cascade-layers"
+ },
+ "keywords": [
+ "postcss-plugin"
+ ],
+ "csstools": {
+ "cssdbId": "cascade-layers",
+ "exportName": "postcssCascadeLayers",
+ "humanReadableName": "PostCSS Cascade Layers",
+ "specUrl": "https://www.w3.org/TR/css-cascade-5/#layering"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ }
}
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 557c543fb..8c51cb5ee 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,6 +1,6 @@
-import { Container } from 'postcss';
+import type { Container, PluginCreator } from 'postcss';
-function postcssCascadeLayers(opts) {
+const creator: PluginCreator = () => {
return {
postcssPlugin: 'postcss-cascade-layers',
Once(root: Container) {
@@ -75,8 +75,8 @@ function postcssCascadeLayers(opts) {
console.log(layerOrder);
},
};
-}
+};
-postcssCascadeLayers.postcss = true;
+creator.postcss = true;
-export default postcssCascadeLayers;
+export default creator;
diff --git a/plugins/postcss-cascade-layers/test/example.css b/plugins/postcss-cascade-layers/test/example.css
deleted file mode 100644
index 181f83a54..000000000
--- a/plugins/postcss-cascade-layers/test/example.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.foo {
- color: red;
-}
-
-.baz {
- color: green;
-}
diff --git a/plugins/postcss-cascade-layers/test/example.expect.css b/plugins/postcss-cascade-layers/test/example.expect.css
deleted file mode 100644
index 181f83a54..000000000
--- a/plugins/postcss-cascade-layers/test/example.expect.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.foo {
- color: red;
-}
-
-.baz {
- color: green;
-}
diff --git a/plugins/postcss-cascade-layers/test/examples/example.css b/plugins/postcss-cascade-layers/test/examples/example.css
new file mode 100644
index 000000000..217b8c85b
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/examples/example.css
@@ -0,0 +1,9 @@
+@layer {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
+}
diff --git a/plugins/postcss-cascade-layers/test/examples/example.expect.css b/plugins/postcss-cascade-layers/test/examples/example.expect.css
new file mode 100644
index 000000000..5370a381a
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/examples/example.expect.css
@@ -0,0 +1,9 @@
+@layer anon0 {
+ target {
+ color: green;
+ }
+}
+
+target {
+ color: purple;
+}
From b2c3f60edb74384e871e7ebd55299b4a74a4b118 Mon Sep 17 00:00:00 2001
From: Jonny Gerig Meyer
Date: Thu, 31 Mar 2022 16:30:44 -0400
Subject: [PATCH 14/33] formatting
---
plugins/postcss-cascade-layers/src/index.ts | 55 +++++++++++----------
1 file changed, 29 insertions(+), 26 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 320f07313..0a9b6d1ea 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,14 +1,14 @@
-import { Container, AtRule, Node} from "postcss";
+import { Container, AtRule, Node } from 'postcss';
function postcssCascadeLayers(opts) {
return {
- postcssPlugin: "postcss-cascade-layers",
+ postcssPlugin: 'postcss-cascade-layers',
Once(root: Container) {
let layerCount = 0;
- let layerOrder = {};
+ const layerOrder = {};
// 1st walkthrough to rename anon layers and store state (no modification of layer styles)
- root.walkAtRules("layer", (atRule) => {
+ root.walkAtRules('layer', (atRule) => {
// give anonymous layers a name
if (!atRule.params) {
atRule.params = `anon${layerCount}`;
@@ -19,9 +19,9 @@ function postcssCascadeLayers(opts) {
// check for where a layer has nested layers AND styles outside of those layers
atRule.each((node) => {
- if (node.type == "atrule") {
+ if (node.type == 'atrule') {
hasNestedLayers = true;
- } else if (node.type == "rule") {
+ } else if (node.type == 'rule') {
hasUnlayeredStyles = true;
}
});
@@ -40,7 +40,7 @@ function postcssCascadeLayers(opts) {
// go through the unlayered rules, clone, and delete from top level atRule
atRule.each((node) => {
- if (node.type == "rule") {
+ if (node.type == 'rule') {
implicitLayer.append(node.clone());
node.remove();
}
@@ -48,7 +48,7 @@ function postcssCascadeLayers(opts) {
}
});
- root.walkAtRules("layer", (layer) => {
+ root.walkAtRules('layer', (layer) => {
layerCount += 1;
layerOrder[layer.params] = layerCount;
});
@@ -66,30 +66,33 @@ function postcssCascadeLayers(opts) {
parent = parent.parent; continue;
}
if ((parent as AtRule).name === 'layer') {
- return true;
- }
- parent = parent.parent;
+ return true;
}
- return false; }
+ parent = parent.parent;
+ }
+ return false;
+ }
- if (!layerCount) {
- // no layers, so nothing to transform.
- return;
- } // 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount)
- root.walkRules((rule) => {
- if (hasLayerAtRuleAncestor(rule)) {
- return;
- } rule.selectors = rule.selectors.map((selector) => {
- // Needs `postcss-selector-parser` to insert `:not()` before any pseudo elements like `::after`
- // This is a side track and can be fixed later.
- return `${generateNot(layerCount)} ${selector}`;
- });
- });
+ if (!layerCount) {
+ // no layers, so nothing to transform.
+ return;
+ }
+
+ // 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount)
+ root.walkRules((rule) => {
+ if (hasLayerAtRuleAncestor(rule)) {
+ return;
+ } rule.selectors = rule.selectors.map((selector) => {
+ // Needs `postcss-selector-parser` to insert `:not()` before any pseudo elements like `::after`
+ // This is a side track and can be fixed later.
+ return `${generateNot(layerCount)} ${selector}`;
+ });
+ });
// 3rd walkthrough to transform layered styles:
// - move out styles from atRule, insert before: https://postcss.org/api/#container-insertbefore
// - delete empty atRule
- // - give selectors the specifity they need based on layerPriority state
+ // - give selectors the specificity they need based on layerPriority state
// root.walkAtRules((atRule) => {
// console.log(atRule, "third walkthrough");
// });
From 19e733cbbbf7b655f9a486218c6163c67348f0fe Mon Sep 17 00:00:00 2001
From: Jonny Gerig Meyer
Date: Thu, 31 Mar 2022 16:37:17 -0400
Subject: [PATCH 15/33] review
---
plugins/postcss-cascade-layers/src/index.ts | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 0a9b6d1ea..9cfedc2dd 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -27,7 +27,7 @@ function postcssCascadeLayers(opts) {
});
if (hasNestedLayers && hasUnlayeredStyles) {
- //create new final layer via cloning, empty it
+ // create new final layer via cloning, empty it
const implicitLayer = atRule.clone({
params: `${atRule.params}-implicit`,
});
@@ -55,15 +55,18 @@ function postcssCascadeLayers(opts) {
// functions for 2nd walkthrough
function generateNot(specificity: number) {
- let list = ''; for (let i = 0; i < specificity; i++) {
+ let list = '';
+ for (let i = 0; i < specificity; i++) {
list += '#\\#'; // something short but still very uncommon
- } return `:not(${list})`;
+ }
+ return `:not(${list})`;
}
function hasLayerAtRuleAncestor(node: Node): boolean {
let parent = node.parent;
while (parent) {
if (parent.type !== 'atrule') {
- parent = parent.parent; continue;
+ parent = parent.parent;
+ continue;
}
if ((parent as AtRule).name === 'layer') {
return true;
@@ -82,7 +85,8 @@ function postcssCascadeLayers(opts) {
root.walkRules((rule) => {
if (hasLayerAtRuleAncestor(rule)) {
return;
- } rule.selectors = rule.selectors.map((selector) => {
+ }
+ rule.selectors = rule.selectors.map((selector) => {
// Needs `postcss-selector-parser` to insert `:not()` before any pseudo elements like `::after`
// This is a side track and can be fixed later.
return `${generateNot(layerCount)} ${selector}`;
From 4717b982ef34bd591dce98a633c92983feeecf9d Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Tue, 5 Apr 2022 20:16:46 +0100
Subject: [PATCH 16/33] fix test
---
.../test/anon-layer.expect.css | 2 +-
.../test/basic.color.expect.css | 2 +-
.../test/basic.expect.css | 2 +-
.../test/examples/example.expect.css | 2 +-
.../test/nested.expect.css | 40 ++++---------------
5 files changed, 12 insertions(+), 36 deletions(-)
diff --git a/plugins/postcss-cascade-layers/test/anon-layer.expect.css b/plugins/postcss-cascade-layers/test/anon-layer.expect.css
index b30e8198c..0c03fc94f 100644
--- a/plugins/postcss-cascade-layers/test/anon-layer.expect.css
+++ b/plugins/postcss-cascade-layers/test/anon-layer.expect.css
@@ -22,6 +22,6 @@
}
}
-target {
+:not(#\##\##\##\#) target {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.color.expect.css b/plugins/postcss-cascade-layers/test/basic.color.expect.css
index 8c2d3172e..e888eb114 100644
--- a/plugins/postcss-cascade-layers/test/basic.color.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.color.expect.css
@@ -10,6 +10,6 @@
}
}
-target {
+:not(#\##\#) target {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.expect.css b/plugins/postcss-cascade-layers/test/basic.expect.css
index e9fbe9616..e888eb114 100644
--- a/plugins/postcss-cascade-layers/test/basic.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.expect.css
@@ -10,6 +10,6 @@
}
}
-:not('#\\##\\#') target {
+:not(#\##\#) target {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/examples/example.expect.css b/plugins/postcss-cascade-layers/test/examples/example.expect.css
index 5370a381a..5992d061d 100644
--- a/plugins/postcss-cascade-layers/test/examples/example.expect.css
+++ b/plugins/postcss-cascade-layers/test/examples/example.expect.css
@@ -4,6 +4,6 @@
}
}
-target {
+:not(#\#) target {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/nested.expect.css b/plugins/postcss-cascade-layers/test/nested.expect.css
index 31b40d981..75b67ed84 100644
--- a/plugins/postcss-cascade-layers/test/nested.expect.css
+++ b/plugins/postcss-cascade-layers/test/nested.expect.css
@@ -1,33 +1,7 @@
@layer A {
-
- @layer Z {
- target {
- color: yellow;
- }
- }@layer A-implicit {
- target {
- color: red;
- }
-
- p {
- color: blue;
- }
-
- i {
- color: red;
- }
-}
}
@layer B {
-
- @layer Z {
- target {
- color: yellow;
- }
- }
-
-@layer B-implicit {
@keyframes slide-left {
from {
margin-left: 0;
@@ -37,23 +11,25 @@
margin-left: -100%;
}
}
-}
-}
-
-@layer C {
@layer Z {
target {
color: yellow;
}
}
+}
-@layer C-implicit {
+@layer C {
@media (prefers-color-scheme: dark) {
h1 {
color: red;
background: black;
}
}
-}
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }
}
From c129d951b754d11261dacb4b6a2c12bbd58f0a54 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Tue, 5 Apr 2022 20:25:59 +0100
Subject: [PATCH 17/33] add additional test for unlayered styles
---
plugins/postcss-cascade-layers/.tape.mjs | 3 +++
.../test/unlayered-styles.css | 23 +++++++++++++++++++
.../test/unlayered-styles.expect.css | 23 +++++++++++++++++++
3 files changed, 49 insertions(+)
create mode 100644 plugins/postcss-cascade-layers/test/unlayered-styles.css
create mode 100644 plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
diff --git a/plugins/postcss-cascade-layers/.tape.mjs b/plugins/postcss-cascade-layers/.tape.mjs
index 364936753..381564432 100644
--- a/plugins/postcss-cascade-layers/.tape.mjs
+++ b/plugins/postcss-cascade-layers/.tape.mjs
@@ -20,4 +20,7 @@ postcssTape(plugin)({
'examples/example': {
message: "minimal example",
},
+ 'unlayered-styles': {
+ message: 'supports unlayered styles alonsgide layers',
+ },
});
diff --git a/plugins/postcss-cascade-layers/test/unlayered-styles.css b/plugins/postcss-cascade-layers/test/unlayered-styles.css
new file mode 100644
index 000000000..312972877
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/unlayered-styles.css
@@ -0,0 +1,23 @@
+@layer A {
+ target {
+ color: red;
+ }
+}
+
+@layer {
+ target {
+ color: green;
+ }
+}
+
+a {
+ color: purple;
+}
+
+target {
+ color: green;
+}
+
+h1, p {
+ color:black
+}
diff --git a/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css b/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
new file mode 100644
index 000000000..c35397f3f
--- /dev/null
+++ b/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
@@ -0,0 +1,23 @@
+@layer A {
+ target {
+ color: red;
+ }
+}
+
+@layer anon0 {
+ target {
+ color: green;
+ }
+}
+
+:not(#\##\#) a {
+ color: purple;
+}
+
+:not(#\##\#) target {
+ color: green;
+}
+
+:not(#\##\#) h1, :not(#\##\#) p {
+ color:black
+}
From 4543413385ce69010587a55cf05e2b98a0cbe3f9 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Wed, 6 Apr 2022 00:57:43 +0100
Subject: [PATCH 18/33] change rule
---
plugins/postcss-cascade-layers/test/unlayered-styles.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/postcss-cascade-layers/test/unlayered-styles.css b/plugins/postcss-cascade-layers/test/unlayered-styles.css
index 312972877..f38eb053d 100644
--- a/plugins/postcss-cascade-layers/test/unlayered-styles.css
+++ b/plugins/postcss-cascade-layers/test/unlayered-styles.css
@@ -18,6 +18,6 @@ target {
color: green;
}
-h1, p {
+span h1, span p {
color:black
}
From 3e444a87f946e42983cf17ebeeef71cc0cc9e760 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Wed, 6 Apr 2022 01:01:36 +0100
Subject: [PATCH 19/33] changing color of rule to see if it's interfering
---
plugins/postcss-cascade-layers/test/unlayered-styles.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/postcss-cascade-layers/test/unlayered-styles.css b/plugins/postcss-cascade-layers/test/unlayered-styles.css
index f38eb053d..5d8f24833 100644
--- a/plugins/postcss-cascade-layers/test/unlayered-styles.css
+++ b/plugins/postcss-cascade-layers/test/unlayered-styles.css
@@ -19,5 +19,5 @@ target {
}
span h1, span p {
- color:black
+ color:red;
}
From d4815ede200c0b21d1d3260ab54d3bfcc409da75 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Wed, 6 Apr 2022 01:05:18 +0100
Subject: [PATCH 20/33] test changes
---
.../postcss-cascade-layers/test/unlayered-styles.expect.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css b/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
index c35397f3f..9a15dbcd4 100644
--- a/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
+++ b/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
@@ -18,6 +18,6 @@
color: green;
}
-:not(#\##\#) h1, :not(#\##\#) p {
- color:black
+:not(#\##\#) span h1, :not(#\##\#) span p {
+ color:red;
}
From 5c1611e3f8b392eda4ff429c29001647b2a25ca4 Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Wed, 6 Apr 2022 08:43:32 +0200
Subject: [PATCH 21/33] cascade layers : fix tests
---
plugins/postcss-cascade-layers/src/index.ts | 59 ++++++++++---------
.../test/anon-layer.expect.css | 2 +-
.../test/basic.color.expect.css | 2 +-
.../test/basic.expect.css | 2 +-
.../test/examples/example.expect.css | 2 +-
.../test/nested.expect.css | 18 ++++++
.../test/unlayered-styles.expect.css | 6 +-
7 files changed, 57 insertions(+), 34 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index 78787d823..e4da9912d 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -35,6 +35,7 @@ const creator: PluginCreator = () => {
const implicitLayer = atRule.clone({
params: `${atRule.params}-implicit`,
});
+
implicitLayer.each((node) => {
node.remove();
});
@@ -44,9 +45,8 @@ const creator: PluginCreator = () => {
// go through the unlayered rules, clone, and delete from top level atRule
atRule.each((node) => {
- if (node.type == 'rule') {
- implicitLayer.append(node.clone());
- node.remove();
+ if (node.type == 'atrule' && node.name === 'layer') {
+ return;
}
implicitLayer.append(node.clone());
@@ -60,29 +60,6 @@ const creator: PluginCreator = () => {
layerOrder[layer.params] = layerCount;
});
- // functions for 2nd walkthrough
- function generateNot(specificity: number) {
- let list = '';
- for (let i = 0; i < specificity; i++) {
- list += '#\\#'; // something short but still very uncommon
- }
- return `:not(${list})`;
- }
- function hasLayerAtRuleAncestor(node: Node): boolean {
- let parent = node.parent;
- while (parent) {
- if (parent.type !== 'atrule') {
- parent = parent.parent;
- continue;
- }
- if ((parent as AtRule).name === 'layer') {
- return true;
- }
- parent = parent.parent;
- }
- return false;
- }
-
if (!layerCount) {
// no layers, so nothing to transform.
return;
@@ -93,10 +70,11 @@ const creator: PluginCreator = () => {
if (hasLayerAtRuleAncestor(rule)) {
return;
}
+
rule.selectors = rule.selectors.map((selector) => {
// Needs `postcss-selector-parser` to insert `:not()` before any pseudo elements like `::after`
// This is a side track and can be fixed later.
- return `${generateNot(layerCount)} ${selector}`;
+ return `${selector}${generateNot(layerCount)}`;
});
});
@@ -114,3 +92,30 @@ const creator: PluginCreator = () => {
creator.postcss = true;
export default creator;
+
+function generateNot(specificity: number) {
+ let list = '';
+ for (let i = 0; i < specificity; i++) {
+ list += '#\\#'; // something short but still very uncommon
+ }
+
+ return `:not(${list})`;
+}
+
+function hasLayerAtRuleAncestor(node: Node): boolean {
+ let parent = node.parent;
+ while (parent) {
+ if (parent.type !== 'atrule') {
+ parent = parent.parent;
+ continue;
+ }
+
+ if ((parent as AtRule).name === 'layer') {
+ return true;
+ }
+
+ parent = parent.parent;
+ }
+
+ return false;
+}
diff --git a/plugins/postcss-cascade-layers/test/anon-layer.expect.css b/plugins/postcss-cascade-layers/test/anon-layer.expect.css
index 0c03fc94f..0338108b8 100644
--- a/plugins/postcss-cascade-layers/test/anon-layer.expect.css
+++ b/plugins/postcss-cascade-layers/test/anon-layer.expect.css
@@ -22,6 +22,6 @@
}
}
-:not(#\##\##\##\#) target {
+target:not(#\##\##\##\#) {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.color.expect.css b/plugins/postcss-cascade-layers/test/basic.color.expect.css
index e888eb114..51a83a9c5 100644
--- a/plugins/postcss-cascade-layers/test/basic.color.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.color.expect.css
@@ -10,6 +10,6 @@
}
}
-:not(#\##\#) target {
+target:not(#\##\#) {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/basic.expect.css b/plugins/postcss-cascade-layers/test/basic.expect.css
index e888eb114..51a83a9c5 100644
--- a/plugins/postcss-cascade-layers/test/basic.expect.css
+++ b/plugins/postcss-cascade-layers/test/basic.expect.css
@@ -10,6 +10,6 @@
}
}
-:not(#\##\#) target {
+target:not(#\##\#) {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/examples/example.expect.css b/plugins/postcss-cascade-layers/test/examples/example.expect.css
index 5992d061d..6cc824c2c 100644
--- a/plugins/postcss-cascade-layers/test/examples/example.expect.css
+++ b/plugins/postcss-cascade-layers/test/examples/example.expect.css
@@ -4,6 +4,6 @@
}
}
-:not(#\#) target {
+target:not(#\#) {
color: purple;
}
diff --git a/plugins/postcss-cascade-layers/test/nested.expect.css b/plugins/postcss-cascade-layers/test/nested.expect.css
index 75b67ed84..e39b54df1 100644
--- a/plugins/postcss-cascade-layers/test/nested.expect.css
+++ b/plugins/postcss-cascade-layers/test/nested.expect.css
@@ -1,4 +1,22 @@
@layer A {
+
+ @layer Z {
+ target {
+ color: yellow;
+ }
+ }@layer A-implicit {
+ target {
+ color: red;
+ }
+
+ p {
+ color: blue;
+ }
+
+ i {
+ color: red;
+ }
+}
}
@layer B {
diff --git a/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css b/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
index 9a15dbcd4..0e10d1a54 100644
--- a/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
+++ b/plugins/postcss-cascade-layers/test/unlayered-styles.expect.css
@@ -10,14 +10,14 @@
}
}
-:not(#\##\#) a {
+a:not(#\##\#) {
color: purple;
}
-:not(#\##\#) target {
+target:not(#\##\#) {
color: green;
}
-:not(#\##\#) span h1, :not(#\##\#) span p {
+span h1:not(#\##\#), span p:not(#\##\#) {
color:red;
}
From 27c309def1cd0a6e38765c74b1ca9ffbf11bd718 Mon Sep 17 00:00:00 2001
From: Olu Niyi-Awosusi
Date: Wed, 20 Apr 2022 23:02:32 +0100
Subject: [PATCH 22/33] first attempt
---
plugins/postcss-cascade-layers/src/index.ts | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index e4da9912d..ad493d7b2 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,4 +1,4 @@
-import { Container, AtRule, Node, PluginCreator } from 'postcss';
+import { Container, AtRule, Node, PluginCreator, decl, Declaration, Rule } from 'postcss';
const creator: PluginCreator = () => {
return {
postcssPlugin: 'postcss-cascade-layers',
@@ -6,6 +6,7 @@ const creator: PluginCreator = () => {
let layerCount = 0;
const layerOrder = {};
+
// 1st walkthrough to rename anon layers and store state (no modification of layer styles)
root.walkAtRules('layer', (atRule) => {
// give anonymous layers a name
@@ -82,9 +83,11 @@ const creator: PluginCreator = () => {
// - move out styles from atRule, insert before: https://postcss.org/api/#container-insertbefore
// - delete empty atRule
// - give selectors the specificity they need based on layerPriority state
- // root.walkAtRules((atRule) => {
- // console.log(atRule, "third walkthrough");
- // });
+ root.walkAtRules('layer', (atRule) => {
+ // move out styles from atRule, insert before
+
+ atRule.replaceWith(new Rule({nodes: atRule.nodes, source: atRule.source, selector: `${generateNot(layerOrder[atRule.params])}`}));
+ });
},
};
};
From a8568ada6d13d2ca9405f1afb15cbedcfa364deb Mon Sep 17 00:00:00 2001
From: romainmenke
Date: Fri, 22 Apr 2022 15:01:27 +0200
Subject: [PATCH 23/33] implement specificity adjustments and rule re-ordering
---
plugins/postcss-cascade-layers/.tape.mjs | 12 +-
plugins/postcss-cascade-layers/README.md | 15 +-
plugins/postcss-cascade-layers/package.json | 4 +
.../src/adjust-selector-specificity.ts | 27 ++
.../src/compound-selector-order.ts | 88 +++++
.../src/desugar-nested-layers.ts | 61 ++++
.../src/has-layer-atrule-ancestor.ts | 20 ++
plugins/postcss-cascade-layers/src/index.ts | 302 ++++++++++++++----
plugins/postcss-cascade-layers/src/model.ts | 194 +++++++++++
.../src/some-in-tree.ts | 14 +
.../postcss-cascade-layers/src/specificity.ts | 111 +++++++
.../postcss-cascade-layers/test/_browser.mjs | 42 +--
.../test/anon-layer.expect.css | 15 +-
.../postcss-cascade-layers/test/atrules.css | 43 +++
.../test/atrules.expect.css | 32 ++
.../test/basic.color.expect.css | 11 +-
plugins/postcss-cascade-layers/test/basic.css | 4 +-
.../test/basic.expect.css | 11 +-
.../test/examples/example.css | 8 +-
.../test/examples/example.expect.css | 10 +-
.../test/invalid-rules.css | 9 +
.../test/invalid-rules.expect.css | 9 +
.../postcss-cascade-layers/test/nested.css | 18 ++
.../test/nested.expect.css | 56 ++--
.../test/unlayered-styles.expect.css | 7 +-
.../wpt/layer-counter-style-override.html | 7 +-
.../test/wpt/layer-important.html | 103 ++++++
.../test/wpt/layer-keyframes-override.html | 170 ++++++++++
.../test/wpt/layer-media-query.html | 155 +++++++++
.../test/wpt/layer-property-override.html | 186 +++++++++++
.../test/wpt/layer-vs-inline-style.html | 84 +++++
rollup/presets/package-typescript.js | 2 +-
32 files changed, 1667 insertions(+), 163 deletions(-)
create mode 100644 plugins/postcss-cascade-layers/src/adjust-selector-specificity.ts
create mode 100644 plugins/postcss-cascade-layers/src/compound-selector-order.ts
create mode 100644 plugins/postcss-cascade-layers/src/desugar-nested-layers.ts
create mode 100644 plugins/postcss-cascade-layers/src/has-layer-atrule-ancestor.ts
create mode 100644 plugins/postcss-cascade-layers/src/model.ts
create mode 100644 plugins/postcss-cascade-layers/src/some-in-tree.ts
create mode 100644 plugins/postcss-cascade-layers/src/specificity.ts
create mode 100644 plugins/postcss-cascade-layers/test/atrules.css
create mode 100644 plugins/postcss-cascade-layers/test/atrules.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/invalid-rules.css
create mode 100644 plugins/postcss-cascade-layers/test/invalid-rules.expect.css
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-important.html
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-keyframes-override.html
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-media-query.html
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-property-override.html
create mode 100644 plugins/postcss-cascade-layers/test/wpt/layer-vs-inline-style.html
diff --git a/plugins/postcss-cascade-layers/.tape.mjs b/plugins/postcss-cascade-layers/.tape.mjs
index 381564432..5d285eb40 100644
--- a/plugins/postcss-cascade-layers/.tape.mjs
+++ b/plugins/postcss-cascade-layers/.tape.mjs
@@ -11,16 +11,22 @@ postcssTape(plugin)({
color: 'purple'
}
},
+ atrules: {
+ message: "supports @keyframes usage",
+ },
nested: {
- message: "supporting nested layer usage",
+ message: "supports nested layer usage",
},
'anon-layer': {
- message: "supporting anonymous layer usage",
+ message: "supports anonymous layer usage",
},
'examples/example': {
message: "minimal example",
},
'unlayered-styles': {
- message: 'supports unlayered styles alonsgide layers',
+ message: 'supports unlayered styles alongside layers',
+ },
+ 'invalid-rules': {
+ message: 'correctly handles invalid rules',
},
});
diff --git a/plugins/postcss-cascade-layers/README.md b/plugins/postcss-cascade-layers/README.md
index cba698666..d632f2934 100644
--- a/plugins/postcss-cascade-layers/README.md
+++ b/plugins/postcss-cascade-layers/README.md
@@ -8,27 +8,28 @@
[PostCSS Cascade Layers] lets use `@layer` following the [Cascade Layers Specification].
```pcss
+
+target {
+ color: purple;
+}
+
@layer {
target {
color: green;
}
}
-target {
- color: purple;
-}
/* becomes */
-@layer anon0 {
+
target {
color: green;
}
-}
-
-target {
+target:not(#\#) {
color: purple;
}
+
```
## Usage
diff --git a/plugins/postcss-cascade-layers/package.json b/plugins/postcss-cascade-layers/package.json
index 4ab887aa2..846cf9a65 100644
--- a/plugins/postcss-cascade-layers/package.json
+++ b/plugins/postcss-cascade-layers/package.json
@@ -24,6 +24,10 @@
"README.md",
"dist"
],
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10",
+ "postcss-value-parser": "^4.2.0"
+ },
"peerDependencies": {
"postcss": "^8.3"
},
diff --git a/plugins/postcss-cascade-layers/src/adjust-selector-specificity.ts b/plugins/postcss-cascade-layers/src/adjust-selector-specificity.ts
new file mode 100644
index 000000000..f2a9e9296
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/adjust-selector-specificity.ts
@@ -0,0 +1,27 @@
+import selectorParser from 'postcss-selector-parser';
+import { sortCompoundSelectorsInsideComplexSelector } from './compound-selector-order';
+
+export function adjustSelectorSpecificity(selector: string, amount: number): string {
+ const selectorAST = selectorParser().astSync(selector + generateNot(amount));
+
+ selectorAST.walk((node) => {
+ if ('nodes' in node) {
+ sortCompoundSelectorsInsideComplexSelector(node);
+ }
+ });
+
+ return selectorAST.toString();
+}
+
+function generateNot(specificity: number) {
+ if (specificity === 0) {
+ return '';
+ }
+
+ let list = '';
+ for (let i = 0; i < specificity; i++) {
+ list += '#\\#'; // something short but still very uncommon
+ }
+
+ return `:not(${list})`;
+}
diff --git a/plugins/postcss-cascade-layers/src/compound-selector-order.ts b/plugins/postcss-cascade-layers/src/compound-selector-order.ts
new file mode 100644
index 000000000..379f8f6fe
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/compound-selector-order.ts
@@ -0,0 +1,88 @@
+import selectorParser from 'postcss-selector-parser';
+
+export function sortCompoundSelectorsInsideComplexSelector(node) {
+ if (!node || !node.nodes) {
+ return;
+ }
+
+ let compound = [];
+ const nodes = [...node.nodes];
+
+ for (let i = 0; i < (nodes.length + 1); i++) {
+ const child = nodes[i];
+ if (!child || child.type === 'combinator') {
+ if (compound.length > 1) {
+ const compoundSelector = selectorParser.selector({ value: '' });
+ compound[0].replaceWith(compoundSelector);
+
+ compound.slice(1).forEach((compoundPart) => {
+ compoundPart.remove();
+ });
+
+ compound.forEach((compoundPart) => {
+ compoundSelector.append(compoundPart);
+ });
+
+ sortCompoundSelector(compoundSelector);
+ compoundSelector.replaceWith(...(compoundSelector.nodes));
+ }
+
+ compound = [];
+ continue;
+ }
+
+ compound.push(child);
+ }
+}
+
+export function sortCompoundSelector(node) {
+ if (!node || !node.nodes) {
+ return;
+ }
+ // simply concatenating with selectors can lead to :
+ // `.fooh1`
+ //
+ // applying a sort where tag selectors are first will result in :
+ // `h1.foo`
+
+ node.nodes.sort((a, b) => {
+ if (a.type === 'selector' && b.type === 'selector' && a.nodes.length && b.nodes.length) {
+ return selectorTypeOrder(a.nodes[0].value, a.nodes[0].type) - selectorTypeOrder(b.nodes[0].value, b.nodes[0].type);
+ }
+
+ if (a.type === 'selector' && a.nodes.length) {
+ return selectorTypeOrder(a.nodes[0].value, a.nodes[0].type) - selectorTypeOrder(b.value, b.type);
+ }
+
+ if (b.type === 'selector' && b.nodes.length) {
+ return selectorTypeOrder(a.value, a.type) - selectorTypeOrder(b.nodes[0].value, b.nodes[0].type);
+ }
+
+ return selectorTypeOrder(a.value, a.type) - selectorTypeOrder(b.value, b.type);
+ });
+}
+
+function selectorTypeOrder(selector, type) {
+ if (selectorParser.isPseudoElement(selector)) {
+ return selectorTypeOrderIndex['pseudoElement'];
+ }
+
+ if (type === 'pseudo' && selector && selector.indexOf('::') === 0) {
+ return selectorTypeOrderIndex['pseudoElement'];
+ }
+ return selectorTypeOrderIndex[type];
+}
+
+const selectorTypeOrderIndex = {
+ universal: 0,
+ tag: 1,
+ id: 2,
+ class: 3,
+ attribute: 4,
+ selector: 5,
+ pseudo: 6,
+ pseudoElement: 7,
+ string: 8,
+ root: 9,
+ comment: 10,
+};
diff --git a/plugins/postcss-cascade-layers/src/desugar-nested-layers.ts b/plugins/postcss-cascade-layers/src/desugar-nested-layers.ts
new file mode 100644
index 000000000..8f657691b
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/desugar-nested-layers.ts
@@ -0,0 +1,61 @@
+import type { Container, AtRule, ChildNode } from 'postcss';
+import { Model } from './model';
+import { someInTree } from './some-in-tree';
+
+export function desugarNestedLayers(root: Container, model: Model) {
+ while (someInTree(root, (node) => {
+ return node.type === 'atrule' && node.nodes && someInTree(node, (nested) => {
+ return nested.type === 'atrule' && nested.name === 'layer';
+ });
+ })) {
+ let foundUnexpectedLayerNesting = false;
+
+ root.walkAtRules('layer', (layerRule) => {
+ if (layerRule.parent === root) {
+ return;
+ }
+
+ if (layerRule.parent.type === 'atrule' && (layerRule.parent as AtRule).name === 'layer') {
+ const parent = layerRule.parent as AtRule;
+
+ model.layerNameParts.set(`${parent.params}.${layerRule.params}`, [...model.layerNameParts.get(parent.params), ...model.layerNameParts.get(layerRule.params)]);
+ model.layerParamsParsed.set(`${parent.params}.${layerRule.params}`, [`${parent.params}.${layerRule.params}`]);
+
+ layerRule.params = `${parent.params}.${layerRule.params}`;
+
+ parent.before(layerRule);
+ if (parent.nodes.length === 0) {
+ parent.remove();
+ }
+
+ return;
+ }
+
+ if (layerRule.parent.type === 'atrule') {
+ const parent = layerRule.parent as AtRule;
+ const parentClone = parent.clone();
+ const layerRuleClone = layerRule.clone();
+
+ parentClone.removeAll();
+
+ layerRuleClone.removeAll();
+ parentClone.append(layerRule.nodes);
+
+ layerRuleClone.append(parentClone);
+ parent.before(layerRuleClone);
+
+ layerRule.remove();
+ if (parent.nodes.length === 0) {
+ parent.remove();
+ }
+ return;
+ }
+
+ foundUnexpectedLayerNesting = true;
+ });
+
+ if (foundUnexpectedLayerNesting) {
+ break;
+ }
+ }
+}
diff --git a/plugins/postcss-cascade-layers/src/has-layer-atrule-ancestor.ts b/plugins/postcss-cascade-layers/src/has-layer-atrule-ancestor.ts
new file mode 100644
index 000000000..2a2af6027
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/has-layer-atrule-ancestor.ts
@@ -0,0 +1,20 @@
+import type { AtRule, Node } from 'postcss';
+
+// Returns the first ancestor of the current node that is an @layer rule.
+export function getLayerAtRuleAncestor(node: Node): AtRule | null {
+ let parent = node.parent;
+ while (parent) {
+ if (parent.type !== 'atrule') {
+ parent = parent.parent;
+ continue;
+ }
+
+ if ((parent as AtRule).name === 'layer') {
+ return parent as AtRule;
+ }
+
+ parent = parent.parent;
+ }
+
+ return null;
+}
diff --git a/plugins/postcss-cascade-layers/src/index.ts b/plugins/postcss-cascade-layers/src/index.ts
index ad493d7b2..a8e205ab8 100644
--- a/plugins/postcss-cascade-layers/src/index.ts
+++ b/plugins/postcss-cascade-layers/src/index.ts
@@ -1,28 +1,98 @@
-import { Container, AtRule, Node, PluginCreator, decl, Declaration, Rule } from 'postcss';
+import type { Container, AtRule, Node, PluginCreator } from 'postcss';
+import selectorParser from 'postcss-selector-parser';
+import { adjustSelectorSpecificity } from './adjust-selector-specificity';
+import { desugarNestedLayers } from './desugar-nested-layers';
+import { getLayerAtRuleAncestor } from './has-layer-atrule-ancestor';
+import { Model } from './model';
+import { someInTree } from './some-in-tree';
+import { selectorSpecificity } from './specificity';
+
const creator: PluginCreator = () => {
return {
postcssPlugin: 'postcss-cascade-layers',
Once(root: Container) {
- let layerCount = 0;
- const layerOrder = {};
+ const model = new Model();
+
+ // - parse layer names
+ // - rename anon layers
+ // - handle empty layers
+ root.walkAtRules('layer', (layerRule) => {
+ if (layerRule.params) {
+ const layerNameList: Array = [];
+ let isInvalidLayerName = false;
+
+ selectorParser().astSync(layerRule.params).each((selector) => {
+ const currentLayerNameParts: Array = [];
+
+ selector.walk((node) => {
+ switch (node.type) {
+ case 'class':
+ currentLayerNameParts.push(node.value);
+ break;
+ case 'tag':
+ currentLayerNameParts.push(node.value);
+ break;
+ default:
+ isInvalidLayerName = true;
+ break;
+ }
+ });
+
+ if (isInvalidLayerName) {
+ return;
+ }
+
+ layerNameList.push(currentLayerNameParts.join('.'));
+
+ model.addLayerNameParts(currentLayerNameParts);
+ });
+
+ model.addLayerParams(layerRule.params, layerNameList);
+ if (layerRule.nodes && layerNameList.length > 1) {
+ // If the layer is a container rule it can not have multiple layer names.
+ isInvalidLayerName = true;
+ }
+
+ if (isInvalidLayerName) {
+ // Set invalid layers to "invalid-layer"
+ // We reset these later.
+ layerRule.name = 'invalid-layer';
+ return;
+ }
+
+ // handle empty layer at-rules.
+ if (!layerRule.nodes || layerRule.nodes.length === 0) {
+ layerNameList.forEach((name) => {
+ model.getLayerNameList(name).forEach((part) => {
+ if (model.layerOrder.has(part)) {
+ return;
+ }
+
+ model.layerOrder.set(part, model.layerCount);
+ model.layerCount += 1;
+ });
+ });
+
+ layerRule.remove();
+ return;
+ }
+ }
- // 1st walkthrough to rename anon layers and store state (no modification of layer styles)
- root.walkAtRules('layer', (atRule) => {
// give anonymous layers a name
- if (!atRule.params) {
- atRule.raws.afterName = ' ';
- atRule.params = `anon${layerCount}`;
+ if (!layerRule.params) {
+ layerRule.raws.afterName = ' ';
+ layerRule.params = model.createAnonymousLayerName();
}
let hasNestedLayers = false;
let hasUnlayeredStyles = false;
// check for where a layer has nested layers AND styles outside of those layers
- atRule.each((node) => {
- if (node.type == 'atrule') {
+ layerRule.each((node) => {
+ if (node.type === 'atrule' && node.name === 'layer') {
hasNestedLayers = true;
- } else if (node.type == 'rule') {
+ } else {
hasUnlayeredStyles = true;
}
@@ -32,61 +102,196 @@ const creator: PluginCreator = () => {
});
if (hasNestedLayers && hasUnlayeredStyles) {
- // create new final layer via cloning, empty it
- const implicitLayer = atRule.clone({
- params: `${atRule.params}-implicit`,
+ // create new final layer via cloning and keep only the styles
+ const implicitLayerName = model.createImplicitLayerName(layerRule.params);
+ const implicitLayer = layerRule.clone({
+ params: implicitLayerName,
});
implicitLayer.each((node) => {
- node.remove();
+ if (node.type === 'atrule' && node.name === 'layer') {
+ node.remove();
+ }
});
// insert new layer
- atRule.append(implicitLayer);
+ layerRule.append(implicitLayer);
- // go through the unlayered rules, clone, and delete from top level atRule
- atRule.each((node) => {
- if (node.type == 'atrule' && node.name === 'layer') {
+ // go through the unlayered rules and delete from top level atRule
+ layerRule.each((node) => {
+ if (node.type === 'atrule' && node.name === 'layer') {
return;
}
- implicitLayer.append(node.clone());
node.remove();
});
}
});
- root.walkAtRules('layer', (layer) => {
- layerCount += 1;
- layerOrder[layer.params] = layerCount;
+ // record layer order
+ root.walkAtRules('layer', (layerRule) => {
+ const currentLayerNameParts = model.getLayerParams(layerRule);
+ const fullLayerName = currentLayerNameParts.join('.');
+ if (model.layerOrder.has(fullLayerName)) {
+ return;
+ }
+
+ if (!model.layerParamsParsed.has(fullLayerName)) {
+ model.layerParamsParsed.set(fullLayerName, [fullLayerName]);
+ }
+
+ if (!model.layerNameParts.has(fullLayerName)) {
+ model.layerNameParts.set(fullLayerName, [...currentLayerNameParts]);
+ }
+
+ model.getLayerNameList(fullLayerName).forEach((name) => {
+ if (model.layerOrder.has(name)) {
+ return;
+ }
+
+ model.layerOrder.set(name, model.layerCount);
+ model.layerCount += 1;
+ });
});
- if (!layerCount) {
+ if (!model.layerCount) {
+ // Reset "invalid-layer" at rules
+ root.walkAtRules('invalid-layer', (layerRule) => {
+ layerRule.name = 'layer';
+ });
+
// no layers, so nothing to transform.
return;
}
- // 2nd walkthrough to transform unlayered styles - need highest specificity (layerCount)
+ // record selector specificity
+ let highestASpecificity = 0;
+ root.walkRules((rule) => {
+ rule.selectors.forEach((selector) => {
+ const specificity = selectorSpecificity(selectorParser().astSync(selector));
+ highestASpecificity = Math.max(highestASpecificity, specificity.a + 1);
+ });
+ });
+
+
+ // transform unlayered styles - need highest specificity (layerCount)
root.walkRules((rule) => {
- if (hasLayerAtRuleAncestor(rule)) {
+ if (getLayerAtRuleAncestor(rule)) {
+ return;
+ }
+
+ // Skip any at rules that do not contain regular declarations (@keyframes)
+ if (rule.parent && rule.parent.type === 'atrule' && (rule.parent as AtRule).name === 'keyframes') {
return;
}
rule.selectors = rule.selectors.map((selector) => {
- // Needs `postcss-selector-parser` to insert `:not()` before any pseudo elements like `::after`
- // This is a side track and can be fixed later.
- return `${selector}${generateNot(layerCount)}`;
+ return adjustSelectorSpecificity(selector, model.layerCount * highestASpecificity);
});
});
- // 3rd walkthrough to transform layered styles:
- // - move out styles from atRule, insert before: https://postcss.org/api/#container-insertbefore
- // - delete empty atRule
+ // Sort layer names
+ model.sortLayerNames();
+
+ // Desugar nested layers
+ desugarNestedLayers(root, model);
+
+ // Transform order of CSS
+ // - split selector rules from non-selector rules
+ // - sort non-selector rules
+ {
+ // Separate selector rules from other rules
+ root.walkAtRules('layer', (layerRule) => {
+ const withSelectorRules = layerRule.clone();
+ const withoutSelectorRules = layerRule.clone();
+
+ withSelectorRules.walkAtRules((atRule) => {
+ if (atRule.name === 'keyframes') {
+ const parent = atRule.parent;
+ atRule.remove();
+ if (parent.nodes.length === 0) {
+ parent.remove();
+ }
+
+ return;
+ }
+
+ if (someInTree(atRule, (node) => {
+ return node.type === 'rule';
+ })) {
+ return;
+ }
+
+ const parent = atRule.parent;
+ atRule.remove();
+ if (parent.nodes.length === 0) {
+ parent.remove();
+ }
+ });
+
+ withoutSelectorRules.walkRules((rule) => {
+ if (rule.parent && rule.parent.type === 'atrule' && (rule.parent as AtRule).name === 'keyframes') {
+ return;
+ }
+
+ const parent = rule.parent;
+ rule.remove();
+ if (parent.nodes.length === 0) {
+ parent.remove();
+ }
+ });
+
+ withSelectorRules.name = 'layer-with-selector-rules';
+
+ layerRule.replaceWith(withSelectorRules, withoutSelectorRules);
+ if (withSelectorRules.nodes.length === 0) {
+ withSelectorRules.remove();
+ }
+
+ if (withoutSelectorRules.nodes.length === 0) {
+ withoutSelectorRules.remove();
+ }
+ });
+
+ // Sort layer nodes
+ model.sortRootNodes(root.nodes);
+
+ // Reset "layer-with-selector-rules" at rules
+ root.walkAtRules('layer-with-selector-rules', (atRule) => {
+ atRule.name = 'layer';
+ });
+ }
+
+ // transform layered styles:
// - give selectors the specificity they need based on layerPriority state
- root.walkAtRules('layer', (atRule) => {
- // move out styles from atRule, insert before
+ root.walkRules((rule) => {
+ const layerForCurrentRule = getLayerAtRuleAncestor(rule);
+ if (!layerForCurrentRule) {
+ return;
+ }
+
+ // Skip any at rules that do not contain regular declarations (@keyframes)
+ if (rule.parent && rule.parent.type === 'atrule' && (rule.parent as AtRule).name === 'keyframes') {
+ return;
+ }
+
+ const fullLayerName = model.getLayerParams(layerForCurrentRule).join('.');
+ rule.selectors = rule.selectors.map((selector) => {
+ return adjustSelectorSpecificity(selector, model.layerOrder.get(fullLayerName) * highestASpecificity);
+ });
+ });
+
+ // Remove all @layer at-rules
+ // Contained styles are inserted before
+ while (someInTree(root, (node) => node.type === 'atrule' && node.name === 'layer')) {
+ root.walkAtRules('layer', (atRule) => {
+ atRule.replaceWith(atRule.nodes);
+ });
+ }
- atRule.replaceWith(new Rule({nodes: atRule.nodes, source: atRule.source, selector: `${generateNot(layerOrder[atRule.params])}`}));
+ // Reset "invalid-layer" at rules
+ root.walkAtRules('invalid-layer', (atRule) => {
+ atRule.name = 'layer';
});
},
};
@@ -95,30 +300,3 @@ const creator: PluginCreator = () => {
creator.postcss = true;
export default creator;
-
-function generateNot(specificity: number) {
- let list = '';
- for (let i = 0; i < specificity; i++) {
- list += '#\\#'; // something short but still very uncommon
- }
-
- return `:not(${list})`;
-}
-
-function hasLayerAtRuleAncestor(node: Node): boolean {
- let parent = node.parent;
- while (parent) {
- if (parent.type !== 'atrule') {
- parent = parent.parent;
- continue;
- }
-
- if ((parent as AtRule).name === 'layer') {
- return true;
- }
-
- parent = parent.parent;
- }
-
- return false;
-}
diff --git a/plugins/postcss-cascade-layers/src/model.ts b/plugins/postcss-cascade-layers/src/model.ts
new file mode 100644
index 000000000..0a6bd0c0c
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/model.ts
@@ -0,0 +1,194 @@
+import type { AtRule, ChildNode, Node } from 'postcss';
+
+export class Model {
+ anonymousLayerCount = 0;
+ layerCount = 0;
+ layerOrder: Map;
+
+ // List of layer names extracted from @layer params.
+ // example : @layer A, B; -> ['A', 'B']
+ // example : @layer A.B; -> ['A.B']
+ layerParamsParsed: Map>;
+
+ // List of sub layer name parts extracted from a single layer name.
+ // example : @layer A.B {}; -> ['A', 'B']
+ layerNameParts: Map>;
+
+ constructor() {
+ this.anonymousLayerCount = 0;
+ this.layerCount = 0;
+ this.layerOrder = new Map();
+ this.layerParamsParsed = new Map();
+ this.layerNameParts = new Map();
+ }
+
+ createAnonymousLayerName(): string {
+ const name = `anon${this.anonymousLayerCount}`;
+ this.addLayerNameParts(name);
+ this.layerParamsParsed.set(name, [name]);
+
+ this.anonymousLayerCount++;
+
+ return name;
+ }
+
+ createImplicitLayerName(layerName: string): string {
+ const parts = this.layerNameParts.get(layerName);
+ const last = parts[parts.length - 1];
+ const name = `${last}-implicit`;
+
+ this.addLayerNameParts([...parts, name]);
+ this.layerParamsParsed.set(name, [name]);
+
+ return name;
+ }
+
+ addLayerParams(key: string, parts?: string): void
+ addLayerParams(key: string, parts: Array): void
+ addLayerParams(key: string, parts: Array | string | undefined): void {
+ if (!parts) {
+ this.layerParamsParsed.set(key, [key]);
+ return;
+ }
+
+ if (typeof parts === 'string') {
+ this.layerParamsParsed.set(key, [parts]);
+ return;
+ }
+ this.layerParamsParsed.set(key, parts);
+ }
+
+ addLayerNameParts(parts: string): void
+ addLayerNameParts(parts: Array): void
+ addLayerNameParts(parts: Array | string): void {
+ if (typeof parts === 'string') {
+ this.layerNameParts.set(parts, [parts]);
+ return;
+ }
+ this.layerNameParts.set(parts.join('.'), parts);
+ }
+
+ getLayerParams(layer: AtRule): Array {
+ const params: Array = [...this.layerParamsParsed.get(layer.params)];
+
+ let parent: Node = layer.parent;
+ while (parent) {
+ if (parent.type !== 'atrule') {
+ parent = parent.parent;
+ continue;
+ }
+
+ if ((parent as AtRule).name === 'layer') {
+ params.push(...this.layerParamsParsed.get((parent as AtRule).params));
+ }
+
+ parent = parent.parent;
+ }
+
+ // Layer names were collected inside out, so order needs to be reversed.
+ params.reverse();
+
+ // Individual layers can also be specified as `@layer foo.bar {}`.
+ // Joining and splitting by "." ensures that we handle each sub layer.
+ return params.flatMap((param) => {
+ return this.layerNameParts.get(param);
+ });
+ }
+
+ getLayerNameList(layerName: string): Array {
+ const parts = this.layerNameParts.get(layerName);
+ const layerNameList = [];
+ for (let i = 0; i < parts.length; i++) {
+ const partialLayerName = parts.slice(0, i + 1).join('.');
+ if (!this.layerParamsParsed.has(partialLayerName)) {
+ this.layerParamsParsed.set(partialLayerName, [partialLayerName]);
+ }
+
+ if (!this.layerNameParts.has(partialLayerName)) {
+ this.layerNameParts.set(partialLayerName, parts.slice(0, i + 1));
+ }
+
+ layerNameList.push(parts.slice(0, i + 1).join('.'));
+ }
+
+ return layerNameList;
+ }
+
+ sortLayerNames() {
+ for (const [layerName, layerIndex] of this.layerOrder) {
+ const parts = this.layerNameParts.get(layerName);
+
+ for (let i = 1; i < (parts.length); i++) {
+ const parentLayer = parts.slice(0, i).join('.');
+ if (!this.layerOrder.has(parentLayer)) {
+ this.layerOrder.set(parentLayer, layerIndex);
+ }
+ }
+ }
+
+ let layerOrderStructured = Array.from(this.layerOrder.entries());
+ layerOrderStructured = layerOrderStructured.sort((a, b) => {
+ const aParts = this.layerNameParts.get(a[0]);
+ const bParts = this.layerNameParts.get(b[0]);
+ if (aParts[0] !== bParts[0]) {
+ return this.layerOrder.get(aParts[0]) - this.layerOrder.get(bParts[0]);
+ }
+
+ const len = Math.max(aParts.length, bParts.length);
+ for (let i = 0; i < len; i++) {
+ const aPart = aParts[i];
+ const bPart = bParts[i];
+ if (aPart === bPart) {
+ continue;
+ }
+
+ if (!aPart) {
+ return 1;
+ }
+
+ if (!bPart) {
+ return -1;
+ }
+
+ return this.layerOrder.get(aParts.slice(0, i).join('.')) - this.layerOrder.get(bParts.slice(0, i).join('.'));
+ }
+ });
+
+ this.layerOrder.clear();
+ layerOrderStructured.forEach((pair, index) => {
+ this.layerOrder.set(pair[0], index);
+ });
+ }
+
+ // Sort root nodes to apply the preferred order by layer priority for non-selector rules.
+ // Selector rules are adjusted by specificity.
+ sortRootNodes(rootNodes: Array) {
+ rootNodes.sort((a, b) => {
+ const aIsCharset = a.type === 'atrule' && a.name === 'charset';
+ const bIsCharset = b.type === 'atrule' && b.name === 'charset';
+ if (aIsCharset && bIsCharset) {
+ return 0;
+ } else if (aIsCharset !== bIsCharset) {
+ return aIsCharset ? -1 : 1;
+ }
+
+ const aIsImport = a.type === 'atrule' && a.name === 'import';
+ const bIsImport = b.type === 'atrule' && b.name === 'import';
+ if (aIsImport && bIsImport) {
+ return 0;
+ } else if (aIsImport !== bIsImport) {
+ return aIsImport ? -1 : 1;
+ }
+
+ const aIsLayer = a.type === 'atrule' && a.name === 'layer';
+ const bIsLayer = b.type === 'atrule' && b.name === 'layer';
+ if (aIsLayer && bIsLayer) {
+ return this.layerOrder.get(a.params) - this.layerOrder.get(b.params);
+ } else if (aIsLayer !== bIsLayer) {
+ return aIsLayer ? -1 : 1;
+ }
+
+ return 0;
+ });
+ }
+}
diff --git a/plugins/postcss-cascade-layers/src/some-in-tree.ts b/plugins/postcss-cascade-layers/src/some-in-tree.ts
new file mode 100644
index 000000000..8a4e430a1
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/some-in-tree.ts
@@ -0,0 +1,14 @@
+import type { Container, ChildNode } from 'postcss';
+
+// Walks the container node and returns true when any node passes the condition.
+export function someInTree(container: Container, predicate: (node: ChildNode) => boolean): boolean {
+ let found = false;
+ container.walk((node) => {
+ if (predicate(node)) {
+ found = true;
+ return false;
+ }
+ });
+
+ return found;
+}
diff --git a/plugins/postcss-cascade-layers/src/specificity.ts b/plugins/postcss-cascade-layers/src/specificity.ts
new file mode 100644
index 000000000..9ed2f377e
--- /dev/null
+++ b/plugins/postcss-cascade-layers/src/specificity.ts
@@ -0,0 +1,111 @@
+import selectorParser from 'postcss-selector-parser';
+
+export function selectorSpecificity(node) {
+ let a = 0;
+ let b = 0;
+ let c = 0;
+
+ if (node.type == 'universal') {
+ return {
+ a: 0,
+ b: 0,
+ c: 0,
+ };
+ } else if (node.type === 'id') {
+ a += 1;
+ } else if (node.type === 'tag') {
+ c += 1;
+ } else if (node.type === 'class') {
+ b += 1;
+ } else if (node.type === 'attribute') {
+ b += 1;
+ } else if (node.type === 'pseudo' && node.value.indexOf('::') === 0) {
+ c += 1;
+ } else if (node.type === 'pseudo') {
+ switch (node.value) {
+ case ':after':
+ case ':before':
+ c += 1;
+ break;
+
+ case ':is':
+ case ':has':
+ case ':not':
+ {
+ if (node.nodes && node.nodes.length > 0) {
+ let mostSpecificListItem = {
+ a: 0,
+ b: 0,
+ c: 0,
+ };
+
+ node.nodes.forEach((child) => {
+ const itemSpecificity = selectorSpecificity(child);
+ if (itemSpecificity.a > mostSpecificListItem.a) {
+ mostSpecificListItem = itemSpecificity;
+ return;
+ } else if (itemSpecificity.a < mostSpecificListItem.a) {
+ return;
+ }
+
+ if (itemSpecificity.b > mostSpecificListItem.b) {
+ mostSpecificListItem = itemSpecificity;
+ return;
+ } else if (itemSpecificity.b < mostSpecificListItem.b) {
+ return;
+ }
+
+ if (itemSpecificity.c > mostSpecificListItem.c) {
+ mostSpecificListItem = itemSpecificity;
+ return;
+ }
+ });
+
+ a += mostSpecificListItem.a;
+ b += mostSpecificListItem.b;
+ c += mostSpecificListItem.c;
+ }
+ break;
+ }
+
+ case 'where':
+ break;
+
+ case ':nth-child':
+ case ':nth-last-child':
+ {
+ const ofSeparatorIndex = node.nodes.findIndex((x) => {
+ x.value === 'of';
+ });
+
+ if (ofSeparatorIndex > -1) {
+ const ofSpecificity = selectorSpecificity(selectorParser.selector({ nodes: node.nodes.slice(ofSeparatorIndex + 1), value: '' }));
+ a += ofSpecificity.a;
+ b += ofSpecificity.b;
+ c += ofSpecificity.c;
+ } else {
+ a += a;
+ b += b;
+ c += c;
+ }
+ }
+ break;
+
+ default:
+ b += 1;
+ }
+ } else if (node.nodes && node.nodes.length > 0) {
+ node.nodes.forEach((child) => {
+ const specificity = selectorSpecificity(child);
+ a += specificity.a;
+ b += specificity.b;
+ c += specificity.c;
+ });
+ }
+
+ return {
+ a,
+ b,
+ c,
+ };
+}
diff --git a/plugins/postcss-cascade-layers/test/_browser.mjs b/plugins/postcss-cascade-layers/test/_browser.mjs
index dc3a1c32c..1918458cb 100644
--- a/plugins/postcss-cascade-layers/test/_browser.mjs
+++ b/plugins/postcss-cascade-layers/test/_browser.mjs
@@ -29,20 +29,26 @@ import postcss from 'postcss';
+ important
+ keyframes override
+ media query
+ property override
+ layer vs. inline style
+