CodeSampleX

Ejemplo

postcss 8.5.2: PostCSS executes synchronous visitor plugins inline via the lazy result .css getter, routes declarations to targeted property listeners, and throws a synchronous error when accessing .css before asynchronous plugins resolve

Muestra verificada para npm postcss 8.5.2: PostCSS executes synchronous visitor plugins inline via the lazy result .css getter, routes declarations to…

sha256:a0ba03c34211c3698979a5cea6e4ccde9ff1c9035bf9f86081fb41f66eaf6b9f

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
3
Claves de firma que lo compilaron
3
Entorno declarado node linux x64 node node npm

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-17
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-09-08

Caso

HOW
Objetivo
PostCSS executes synchronous visitor plugins inline via the lazy result .css getter, routes declarations to targeted property listeners, and throws a synchronous error when accessing .css before asynchronous plugins resolve
Paquetes
Símbolos
  • postcss
  • Processor#process
  • Root#walkDecls
  • Declaration
  • Rule
Entorno
node
Creado
2026-08-17T17:28:36Z

Contrato

  1. Accessing LazyResult.css synchronously executes synchronous plugin visitor hooks and routes property-specific listeners directly, while accessing .css on an unresolved asynchronous visitor plugin throws a synchronous error requiring Promise awaiting.
  2. Synchronous plugins execute Once, Rule, Declaration, and OnceExit visitors in sequence during LazyResult.css evaluation.
  3. Targeted Declaration listeners defined by property name (e.g. Declaration.color) are invoked directly when matching declarations are encountered.
  4. Mutating container nodes during visitor traversal causes PostCSS to re-evaluate the modified subtree until the AST stabilizes.
  5. Synchronous access to LazyResult.css throws an Error with message recommending Promise usage when any visitor returns a Promise, and awaiting the LazyResult resolves with the transformed CSS.

Archivos

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Descargar el artefacto de código fuente (tar.gz)

Código fuente

NOTES.md
# PostCSS 8 Plugin Lifecycle and Execution Semantics

## Known Solution Search
A query for known solutions returned published sample `sha256:221c662febb9ecd93d2ebacebe2775d2066adba800247e7c0af8e4a9bb4b411c`, which demonstrates external source map generation using the `map` option.

This sample proves a distinct core subsystem: the PostCSS 8 visitor plugin architecture, targeted property listener routing, synchronous lazy evaluation, AST node manipulation, and asynchronous error handling.

## Pinned Release
- Package: `postcss`
- Exact version: `8.5.2`
- Ecosystem: `npm`
- Runtime: `node`

## Observed Behaviour

### 1. Visitor Plugin Lifecycle and Targeted Declaration Listeners
PostCSS 8 plugins export an object with `postcssPlugin` and visitor methods, marked with `plugin.postcss = true`.
- During CSS processing, PostCSS traverses the AST and invokes lifecycle hooks in order: `Once(root)`, `Rule(rule)`, `Declaration(decl)`, and `OnceExit(root)`.
- Plugins can define targeted declaration listeners under `Declaration` keyed by CSS property name (e.g. `Declaration: { color(decl) {}, 'font-size'(decl) {} }`). PostCSS dispatches these handlers directly when matching declarations are encountered, eliminating the need for manual property checking in generic declaration visitors.

### 2. Subtree Re-Evaluation on Container Mutation
When a visitor mutates a container node (such as rewriting `rule.selector`), PostCSS re-visits the modified subtree to allow subsequent and dependent visitors to inspect the transformed nodes. Traversal terminates when the AST stabilizes without further mutations.

### 3. Synchronous Evaluation via LazyResult.css
When all registered plugins and visitors are synchronous, invoking `postcss(plugins).process(css, { from: undefined })` returns a `LazyResult`. Reading `lazyResult.css` synchronously triggers the full visitor pipeline, AST serialization, and returns the resulting CSS string without creating or requiring Promise handling.

### 4. AST Node Manipulation and Warning Collection
- Nodes can be cloned, inserted, or removed using methods such as `decl.clone()`, `decl.before()`, and `root.walkDecls()`.
- Plugin warnings emitted through `result.warn(message, { node })` populate `result.warnings()` with direct references to the corresponding AST node.

### 5. Asynchronous Plugin Safety Boundary
When any plugin visitor returns a `Promise` (asynchronous execution):
- Accessing `lazyResult.css` synchronously throws an `Error` stating: `Use process(css).then(cb) to work with async plugins`.
- Awaiting the `LazyResult` (`await postcss(plugins).process(...)`) executes the asynchronous pipeline and resolves to the completed `Result` object with the transformed `.css`.
csx.json
{"case":{"caseId":"case:sha256:5d7226fd16b50aa9f7e2d08969d2cf8cc7a063aa0413a5d91fe68689cd062d5c","contract":["Accessing LazyResult.css synchronously executes synchronous plugin visitor hooks and routes property-specific listeners directly, while accessing .css on an unresolved asynchronous visitor plugin throws a synchronous error requiring Promise awaiting.","Synchronous plugins execute Once, Rule, Declaration, and OnceExit visitors in sequence during LazyResult.css evaluation.","Targeted Declaration listeners defined by property name (e.g. Declaration.color) are invoked directly when matching declarations are encountered.","Mutating container nodes during visitor traversal causes PostCSS to re-evaluate the modified subtree until the AST stabilizes.","Synchronous access to LazyResult.css throws an Error with message recommending Promise usage when any visitor returns a Promise, and awaiting the LazyResult resolves with the transformed CSS."],"goal":"PostCSS executes synchronous visitor plugins inline via the lazy result .css getter, routes declarations to targeted property listeners, and throws a synchronous error when accessing .css before asynchronous plugins resolve","kind":"HOW","packages":["pkg:npm/postcss@8.5.2"],"schemaVersion":1,"symbols":["postcss","Processor#process","Root#walkDecls","Declaration","Rule"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/postcss@8.5.2"],"schemaVersion":1,"symbols":["postcss","Processor#process","Root#walkDecls","Declaration","Rule"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "npm-postcss-version-depth",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-postcss-version-depth",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "postcss": "8.5.2"
      }
    },
    "node_modules/nanoid": {
      "version": "3.3.18",
      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
      "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/ai"
        }
      ],
      "license": "MIT",
      "bin": {
        "nanoid": "bin/nanoid.cjs"
      },
      "engines": {
        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
      }
    },
    "node_modules/picocolors": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
      "license": "ISC"
    },
    "node_modules/postcss": {
      "version": "8.5.2",
      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
      "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
      "funding": [
        {
          "type": "opencollective",
          "url": "https://opencollective.com/postcss/"
        },
        {
          "type": "tidelift",
          "url": "https://tidelift.com/funding/github/npm/postcss"
        },
        {
          "type": "github",
          "url": "https://github.com/sponsors/ai"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "nanoid": "^3.3.8",
        "picocolors": "^1.1.1",
        "source-map-js": "^1.2.1"
      },
      "engines": {
        "node": "^10 || ^12 || >=14"
      }
    },
    "node_modules/source-map-js": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
      "license": "BSD-3-Clause",
      "engines": {
        "node": ">=0.10.0"
      }
    }
  }
}
package.json
{
  "name": "npm-postcss-version-depth",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs",
  "dependencies": {
    "postcss": "8.5.2"
  }
}
test/contract.mjs
import assert from 'node:assert/strict';
import postcss from 'postcss';

// Test 1: Synchronous visitor lifecycle and targeted Declaration property hooks
{
  const executionLog = [];

  const transformPlugin = (options = { prefix: 'app-' }) => ({
    postcssPlugin: 'transform-plugin',
    Once(root) {
      executionLog.push('Once');
    },
    Rule(rule) {
      executionLog.push(`Rule:${rule.selector}`);
      if (!rule.selector.startsWith(`.${options.prefix}`)) {
        rule.selector = `.${options.prefix}${rule.selector.replace(/^\./, '')}`;
      }
    },
    Declaration: {
      color(decl) {
        executionLog.push(`Decl:color:${decl.value}`);
        decl.value = 'rgb(0, 0, 255)';
      },
      'font-size'(decl) {
        executionLog.push(`Decl:font-size:${decl.value}`);
        decl.value = '1rem';
      }
    },
    OnceExit(root) {
      executionLog.push('OnceExit');
    }
  });
  transformPlugin.postcss = true;

  const inputCss = '.card { color: red; font-size: 16px; margin: 8px; }';
  const lazyResult = postcss([transformPlugin()]).process(inputCss, { from: undefined });

  // Accessing .css synchronously triggers the processing pipeline
  const outputCss = lazyResult.css;

  assert.strictEqual(
    outputCss,
    '.app-card { color: rgb(0, 0, 255); font-size: 1rem; margin: 8px; }'
  );

  assert.deepStrictEqual(executionLog, [
    'Once',
    'Rule:.card',
    'Decl:color:red',
    'Decl:font-size:16px',
    'Rule:.app-card',
    'Decl:color:rgb(0, 0, 255)',
    'Decl:font-size:1rem',
    'OnceExit'
  ]);
  assert.strictEqual(lazyResult.warnings().length, 0);
}

// Test 2: AST node manipulation (decl.clone, decl.before, and result.warn)
{
  const vendorPrefixPlugin = () => ({
    postcssPlugin: 'vendor-prefix-plugin',
    Declaration: {
      opacity(decl, { result }) {
        result.warn('opacity property prefixed for compatibility', { node: decl });
        const webkitDecl = decl.clone({ prop: '-webkit-opacity', value: decl.value });
        decl.before(webkitDecl);
      }
    },
    Once(root) {
      root.walkDecls('display', (decl) => {
        if (decl.value === 'flex') {
          decl.before(decl.clone({ value: '-webkit-flex' }));
        }
      });
    }
  });
  vendorPrefixPlugin.postcss = true;

  const inputCss = 'div { display: flex; opacity: 0.5; }';
  const result = postcss([vendorPrefixPlugin()]).process(inputCss, { from: undefined });

  assert.strictEqual(
    result.css,
    'div { display: -webkit-flex; display: flex; -webkit-opacity: 0.5; opacity: 0.5; }'
  );

  const warnings = result.warnings();
  assert.strictEqual(warnings.length, 1);
  assert.strictEqual(warnings[0].text, 'opacity property prefixed for compatibility');
  assert.strictEqual(warnings[0].node.prop, 'opacity');
}

// Test 3: Asynchronous plugin execution guard and Promise resolution
{
  const asyncPlugin = () => ({
    postcssPlugin: 'async-plugin',
    async Once(root) {
      await new Promise((resolve) => setTimeout(resolve, 10));
      const footerRule = postcss.rule({ selector: 'footer' });
      footerRule.append(postcss.decl({ prop: 'content', value: '"async-banner"' }));
      root.append(footerRule);
    }
  });
  asyncPlugin.postcss = true;

  // Synchronous access on a LazyResult with async plugins throws descriptive error
  const asyncLazy = postcss([asyncPlugin()]).process('header { color: green; }', { from: undefined });
  assert.throws(
    () => {
      const _ = asyncLazy.css;
    },
    {
      name: 'Error',
      message: /Use process\(css\)\.then\(cb\) to work with async plugins/
    }
  );

  // Awaiting the LazyResult executes asynchronously and resolves with transformed CSS
  const asyncLazyForAwait = postcss([asyncPlugin()]).process('header { color: green; }', { from: undefined });
  const asyncResolved = await asyncLazyForAwait;
  assert.strictEqual(
    asyncResolved.css,
    'header { color: green; }\nfooter { content: "async-banner"; }'
  );
}

console.log('PostCSS contract passed');

Seeder de origen

csx-seed