@ecopages/postcss-processor 0.2.0-alpha.4 → 0.2.0-alpha.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,56 +1,32 @@
1
- # PostCSS Processor
1
+ # @ecopages/postcss-processor
2
2
 
3
- This module provides a PostCSS processor plugin for Ecopages and utility functions for processing CSS files and strings using PostCSS. It includes built-in presets for Tailwind CSS (v3 and v4).
3
+ PostCSS processing pipeline for Ecopages. It provides a processor plugin that seamlessly integrates PostCSS into the Ecopages build system, and includes built-in presets for Tailwind CSS (v3 and v4).
4
4
 
5
5
  ## Features
6
6
 
7
- - **Ecopages Processor Plugin**: Seamless integration with Ecopages build system.
8
- - **Tailwind Presets**: Ready-to-use configurations for Tailwind CSS v3 and v4.
9
- - **Automatic Configuration**: Detects `postcss.config.{js,ts,etc}` automatically.
10
- - **Standalone Usage**: Process CSS files or strings directly.
11
- - **Bun Loader**: Automatically registers a Bun loader for importing CSS in TS/JS files.
7
+ - **Ecopages Processor Plugin**: Hook right into the build pipeline.
8
+ - **Tailwind Presets**: Pre-configured pipelines for Tailwind CSS v3 and v4.
9
+ - **Automatic Configuration**: Detects existing `postcss.config.{js,ts,etc}`.
10
+ - **Bun Loader**: Registers a Bun loader for importing CSS in TS/JS files.
12
11
 
13
- ## Install
12
+ ## Installation
14
13
 
15
14
  ```bash
16
- bunx jsr add @ecopages/postcss-processor
15
+ bun add @ecopages/postcss-processor
17
16
  ```
18
17
 
19
- ## Usage with Ecopages
18
+ ## Usage
20
19
 
21
- Integrate the processor into your `eco.config.ts` using one of the available presets.
22
-
23
- ### Tailwind v3 Preset
24
-
25
- Includes `tailwindcss`, `autoprefixer`, `postcss-import`, `cssnano`.
26
-
27
- ```bash
28
- bun add -D tailwindcss@3.4.19
29
- ```
30
-
31
- ```typescript
32
- // eco.config.ts
33
- import { ConfigBuilder } from '@ecopages/core';
34
- import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
35
- import { tailwindV3Preset } from '@ecopages/postcss-processor/presets/tailwind-v3';
36
-
37
- const config = await new ConfigBuilder().setProcessors([postcssProcessorPlugin(tailwindV3Preset())]).build();
38
-
39
- export default config;
40
- ```
20
+ Integrate the processor into your `eco.config.ts` using a preset.
41
21
 
42
22
  ### Tailwind v4 Preset (Recommended)
43
23
 
44
- Includes `@tailwindcss/postcss`, `autoprefixer`, `postcss-nested`, `cssnano`, and handles `@reference` injection for `@apply`.
45
-
46
- ```bash
47
- bun add -D @tailwindcss/postcss tailwindcss
48
- ```
24
+ Requires `@tailwindcss/postcss` and `tailwindcss` to be installed.
49
25
 
50
26
  ```typescript
51
27
  // eco.config.ts
52
28
  import path from 'node:path';
53
- import { ConfigBuilder } from '@ecopages/core';
29
+ import { ConfigBuilder } from '@ecopages/core/config-builder';
54
30
  import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
55
31
  import { tailwindV4Preset } from '@ecopages/postcss-processor/presets/tailwind-v4';
56
32
 
@@ -58,7 +34,7 @@ const config = await new ConfigBuilder()
58
34
  .setProcessors([
59
35
  postcssProcessorPlugin(
60
36
  tailwindV4Preset({
61
- referencePath: path.resolve(import.meta.dir, 'src/styles/app.css'),
37
+ referencePath: path.resolve(import.meta.dirname, 'src/styles/app.css'),
62
38
  }),
63
39
  ),
64
40
  ])
@@ -67,53 +43,41 @@ const config = await new ConfigBuilder()
67
43
  export default config;
68
44
  ```
69
45
 
70
- ### Browser Support
71
-
72
- By default, the presets target a broad range of modern browsers (`>0.3%, not ie 11, not dead, not op_mini all`).
73
-
74
- To override this, add a `browserslist` configuration to your `package.json` or create a `.browserslistrc` file in your project root. The processor will automatically detect and use your custom configuration.
75
-
76
- ### Custom Configuration
77
-
78
- You can also use a standard `postcss.config.js` file or pass plugins manually.
79
-
80
- **Using `postcss.config.js`:**
81
- Create the file in your root, and simply add `postcssProcessorPlugin()` to your config without arguments.
46
+ ### Tailwind v3 Preset
82
47
 
83
- **Manual Configuration:**
48
+ Requires `tailwindcss@3`, `autoprefixer`, `postcss-import`, and `cssnano` to be installed.
84
49
 
85
50
  ```typescript
51
+ // eco.config.ts
52
+ import { ConfigBuilder } from '@ecopages/core/config-builder';
86
53
  import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
87
- import myPlugin from 'postcss-my-plugin';
54
+ import { tailwindV3Preset } from '@ecopages/postcss-processor/presets/tailwind-v3';
55
+
56
+ const config = await new ConfigBuilder().setProcessors([postcssProcessorPlugin(tailwindV3Preset())]).build();
88
57
 
89
- postcssProcessorPlugin({
90
- plugins: {
91
- 'my-plugin': myPlugin(),
92
- },
93
- });
58
+ export default config;
94
59
  ```
95
60
 
96
- **Advanced Configuration:**
61
+ ## Custom Configuration
97
62
 
98
- For advanced use cases, use transformation hooks to modify CSS before or after processing:
63
+ To use your own `postcss.config.js`, simply call `postcssProcessorPlugin()` without arguments.
64
+
65
+ You can also pass raw plugins or transformation hooks manually:
99
66
 
100
67
  ```typescript
101
- import { ConfigBuilder } from '@ecopages/core';
68
+ import { ConfigBuilder } from '@ecopages/core/config-builder';
102
69
  import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
70
+ import myPlugin from 'postcss-my-plugin';
103
71
 
104
72
  const config = await new ConfigBuilder()
105
73
  .setProcessors([
106
74
  postcssProcessorPlugin({
107
- // Define a filter for files to process (defaults to /\.css$/)
108
75
  filter: /\.css$/,
109
- // Provide a function to transform input before processing
110
- transformInput: async (css) => `/* My Custom Header */\n${css}`,
111
- // Provide a function to transform output after processing
112
- transformOutput: async (css) => css.replace('blue', 'red'),
113
- // Explicitly provide plugins (overrides defaults)
114
76
  plugins: {
115
- /* custom plugins */
77
+ 'my-plugin': myPlugin(),
116
78
  },
79
+ transformInput: async (css) => `/* Header */\n${css}`,
80
+ transformOutput: async (css) => css.replace('blue', 'red'),
117
81
  }),
118
82
  ])
119
83
  .build();
@@ -121,9 +85,9 @@ const config = await new ConfigBuilder()
121
85
  export default config;
122
86
  ```
123
87
 
124
- ## Standalone Usage
88
+ ## Standalone Processing
125
89
 
126
- You can use the underlying processor functions directly:
90
+ You can bypass Ecopages entirely and use the processor utilities directly:
127
91
 
128
92
  ```typescript
129
93
  import { PostCssProcessor } from '@ecopages/postcss-processor';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/postcss-processor",
3
- "version": "0.2.0-alpha.4",
3
+ "version": "0.2.0-alpha.41",
4
4
  "description": "Postcss processor, transform string or postcss file to css",
5
5
  "keywords": [
6
6
  "postcss",
@@ -17,17 +17,17 @@
17
17
  "directory": "packages/processors/postcss-processor"
18
18
  },
19
19
  "dependencies": {
20
- "@ecopages/core": "0.2.0-alpha.4",
21
- "@ecopages/file-system": "0.2.0-alpha.4",
22
- "@ecopages/logger": "latest",
23
- "autoprefixer": "^10.4.0",
24
- "browserslist": "^4.28.1",
25
- "cssnano": "^6.0.0",
26
- "postcss": "^8.4.32",
27
- "postcss-import": "^15.0.0",
20
+ "@ecopages/file-system": "0.2.0-alpha.34",
21
+ "@ecopages/logger": "^0.2.3",
22
+ "autoprefixer": "^10.5.0",
23
+ "browserslist": "^4.28.2",
24
+ "cssnano": "^7.1.9",
25
+ "postcss": "^8.5.14",
26
+ "postcss-import": "^16.1.1",
28
27
  "postcss-nested": "^7.0.2"
29
28
  },
30
29
  "peerDependencies": {
30
+ "@ecopages/core": "0.2.0-alpha.41",
31
31
  "@tailwindcss/postcss": ">=4",
32
32
  "tailwindcss": ">=3"
33
33
  },
package/src/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from './plugin';
2
- export * from './postcss-processor';
1
+ export * from './plugin.js';
2
+ export * from './postcss-processor.js';
package/src/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./plugin";
2
- export * from "./postcss-processor";
1
+ export * from "./plugin.js";
2
+ export * from "./postcss-processor.js";
package/src/plugin.d.ts CHANGED
@@ -2,8 +2,7 @@
2
2
  * PostCssProcessorPlugin
3
3
  * @module @ecopages/postcss-processor
4
4
  */
5
- import { Processor, type ProcessorConfig } from '@ecopages/core/plugins/processor';
6
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
5
+ import { Processor, type EcoBuildPlugin, type ProcessorConfig } from '@ecopages/core/plugins/processor';
7
6
  import type postcss from 'postcss';
8
7
  /**
9
8
  * Record of PostCSS plugins keyed by name
@@ -26,6 +25,13 @@ export interface PostCssProcessorPluginConfig {
26
25
  * Regex filter to match files to process
27
26
  */
28
27
  filter?: RegExp;
28
+ /**
29
+ * CSS entry files to rebuild when a non-CSS dependency changes.
30
+ *
31
+ * Use this when the processor watches template or script files that affect a
32
+ * known stylesheet entry, such as a Tailwind reference file.
33
+ */
34
+ dependencyEntryPaths?: string[];
29
35
  /**
30
36
  * Function to transform the contents of the file.
31
37
  * It can be handy to add a custom header or footer to the file.
@@ -61,16 +67,44 @@ export interface PostCssProcessorPluginConfig {
61
67
  */
62
68
  export declare class PostCssProcessorPlugin extends Processor<PostCssProcessorPluginConfig> {
63
69
  static DEFAULT_OPTIONS: Required<Pick<PostCssProcessorPluginConfig, 'filter'>>;
70
+ private buildContributionsPrepared;
64
71
  private postcssPlugins;
65
72
  private pluginFactories?;
66
73
  private readonly runtimeCssCache;
67
74
  private readonly trackedCssFiles;
68
75
  private watchQueue;
76
+ /**
77
+ * Maps an imported CSS file path → set of tracked CSS entry files that import it.
78
+ * Used to resolve which parent entry files need re-processing when a dependency changes.
79
+ */
80
+ private readonly cssDependencyMap;
69
81
  private getCssFilter;
70
82
  private resolveProcessedCssPath;
71
83
  private readProcessedCssFromDist;
72
84
  private persistProcessedCss;
73
85
  private prewarmRuntimeCssCache;
86
+ /**
87
+ * Regex to match CSS @import statements and extract the path.
88
+ * Handles: @import './foo.css'; @import "./foo.css"; @import url('./foo.css');
89
+ */
90
+ private static readonly CSS_IMPORT_REGEX;
91
+ /**
92
+ * Builds the CSS dependency map by scanning tracked CSS files for @import directives.
93
+ * Maps each imported file to the set of tracked entry files that import it (directly or transitively).
94
+ */
95
+ private buildCssDependencyMap;
96
+ /**
97
+ * Extracts resolved absolute paths of CSS files imported via @import in the given CSS content.
98
+ * Recursively follows imports to capture transitive dependencies.
99
+ * It skips bare module imports like @import 'tailwindcss'.
100
+ * It recursively follows imports to capture transitive dependencies.
101
+ */
102
+ private extractCssImports;
103
+ /**
104
+ * Resolves a changed CSS file to its parent entry file(s) if it is an @import dependency.
105
+ * Returns an empty array if the file is not an import dependency (i.e., it's an entry file itself).
106
+ */
107
+ private resolveEntryFiles;
74
108
  private transformCssSync;
75
109
  private transformCssAsync;
76
110
  matchesFileFilter(filepath: string): boolean;
@@ -78,17 +112,33 @@ export declare class PostCssProcessorPlugin extends Processor<PostCssProcessorPl
78
112
  private refreshConfiguredPlugins;
79
113
  private enqueueWatchTask;
80
114
  private getTrackedCssFiles;
115
+ private getTrackedCssEntryFiles;
116
+ private getDependencyEntryFiles;
81
117
  private handleDependencyChange;
82
118
  constructor(config?: Omit<ProcessorConfig<PostCssProcessorPluginConfig>, 'name' | 'description'>);
83
119
  /**
84
120
  * Handles CSS file changes during development.
85
- * Processes the file and broadcasts a css-update event for hot reloading.
121
+ * If the file is an @import dependency, re-processes the parent entry file(s) instead.
122
+ * Broadcasts a css-update event for hot reloading.
86
123
  */
87
124
  private handleCssChange;
125
+ /**
126
+ * Processes a CSS file and broadcasts a css-update event.
127
+ * Skips broadcast if the processed output hasn't changed.
128
+ */
129
+ private processAndBroadcast;
88
130
  get buildPlugins(): EcoBuildPlugin[];
89
131
  get plugins(): EcoBuildPlugin[];
90
132
  /**
91
- * Setup the PostCSS processor.
133
+ * Resolves the configured PostCSS plugin list before config build seals the
134
+ * app manifest.
135
+ *
136
+ * @remarks
137
+ * Runtime setup reuses this prepared list and only performs cache prewarming.
138
+ */
139
+ prepareBuildContributions(): Promise<void>;
140
+ /**
141
+ * Prepares build contributions if not already done and prewarms the runtime CSS cache.
92
142
  */
93
143
  setup(): Promise<void>;
94
144
  /**
package/src/plugin.js CHANGED
@@ -2,8 +2,8 @@ import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
3
  import { Processor } from "@ecopages/core/plugins/processor";
4
4
  import { Logger } from "@ecopages/logger";
5
- import { PostCssProcessor } from "./postcss-processor";
6
- import { createCssLoaderPlugin } from "./runtime/css-loader-plugin";
5
+ import { PostCssProcessor } from "./postcss-processor.js";
6
+ import { createCssLoaderPlugin } from "./runtime/css-loader-plugin.js";
7
7
  const logger = new Logger("[@ecopages/postcss-processor]", {
8
8
  debug: process.env.ECOPAGES_LOGGER_DEBUG === "true"
9
9
  });
@@ -11,11 +11,17 @@ class PostCssProcessorPlugin extends Processor {
11
11
  static DEFAULT_OPTIONS = {
12
12
  filter: /\.css$/
13
13
  };
14
+ buildContributionsPrepared = false;
14
15
  postcssPlugins = [];
15
16
  pluginFactories;
16
17
  runtimeCssCache = /* @__PURE__ */ new Map();
17
18
  trackedCssFiles = /* @__PURE__ */ new Set();
18
19
  watchQueue = Promise.resolve();
20
+ /**
21
+ * Maps an imported CSS file path → set of tracked CSS entry files that import it.
22
+ * Used to resolve which parent entry files need re-processing when a dependency changes.
23
+ */
24
+ cssDependencyMap = /* @__PURE__ */ new Map();
19
25
  getCssFilter() {
20
26
  return this.options?.filter ?? PostCssProcessorPlugin.DEFAULT_OPTIONS.filter;
21
27
  }
@@ -66,6 +72,69 @@ class PostCssProcessorPlugin extends Processor {
66
72
  this.runtimeCssCache.set(filePath, processed);
67
73
  await this.persistProcessedCss(filePath, processed);
68
74
  }
75
+ this.buildCssDependencyMap();
76
+ }
77
+ /**
78
+ * Regex to match CSS @import statements and extract the path.
79
+ * Handles: @import './foo.css'; @import "./foo.css"; @import url('./foo.css');
80
+ */
81
+ static CSS_IMPORT_REGEX = /@import\s+(?:url\(\s*)?['"]([^'"]+\.css)['"](?:\s*\))?\s*;/gm;
82
+ /**
83
+ * Builds the CSS dependency map by scanning tracked CSS files for @import directives.
84
+ * Maps each imported file to the set of tracked entry files that import it (directly or transitively).
85
+ */
86
+ buildCssDependencyMap() {
87
+ this.cssDependencyMap.clear();
88
+ for (const entryFile of this.trackedCssFiles) {
89
+ if (!fileSystem.exists(entryFile)) continue;
90
+ const rawContents = fileSystem.readFileAsBuffer(entryFile).toString("utf-8");
91
+ const imports = this.extractCssImports(rawContents, entryFile);
92
+ for (const importedFile of imports) {
93
+ if (!this.cssDependencyMap.has(importedFile)) {
94
+ this.cssDependencyMap.set(importedFile, /* @__PURE__ */ new Set());
95
+ }
96
+ this.cssDependencyMap.get(importedFile).add(entryFile);
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Extracts resolved absolute paths of CSS files imported via @import in the given CSS content.
102
+ * Recursively follows imports to capture transitive dependencies.
103
+ * It skips bare module imports like @import 'tailwindcss'.
104
+ * It recursively follows imports to capture transitive dependencies.
105
+ */
106
+ extractCssImports(cssContent, fromFile, visited = /* @__PURE__ */ new Set()) {
107
+ const dir = path.dirname(fromFile);
108
+ const imports = [];
109
+ let match;
110
+ const regex = new RegExp(PostCssProcessorPlugin.CSS_IMPORT_REGEX.source, "gm");
111
+ while ((match = regex.exec(cssContent)) !== null) {
112
+ const importPath = match[1];
113
+ if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
114
+ continue;
115
+ }
116
+ const resolvedPath = path.resolve(dir, importPath);
117
+ if (visited.has(resolvedPath)) continue;
118
+ visited.add(resolvedPath);
119
+ imports.push(resolvedPath);
120
+ if (fileSystem.exists(resolvedPath)) {
121
+ const nestedContent = fileSystem.readFileAsBuffer(resolvedPath).toString("utf-8");
122
+ const nestedImports = this.extractCssImports(nestedContent, resolvedPath, visited);
123
+ imports.push(...nestedImports);
124
+ }
125
+ }
126
+ return imports;
127
+ }
128
+ /**
129
+ * Resolves a changed CSS file to its parent entry file(s) if it is an @import dependency.
130
+ * Returns an empty array if the file is not an import dependency (i.e., it's an entry file itself).
131
+ */
132
+ resolveEntryFiles(filePath) {
133
+ const entries = this.cssDependencyMap.get(filePath);
134
+ if (!entries || entries.size === 0) {
135
+ return [];
136
+ }
137
+ return Array.from(entries);
69
138
  }
70
139
  transformCssSync(input) {
71
140
  const cached = this.runtimeCssCache.get(input.filePath);
@@ -115,11 +184,24 @@ class PostCssProcessorPlugin extends Processor {
115
184
  (filePath) => this.matchesFileFilter(filePath) && fileSystem.exists(filePath)
116
185
  );
117
186
  }
187
+ getTrackedCssEntryFiles() {
188
+ const importedCssFiles = new Set(this.cssDependencyMap.keys());
189
+ return this.getTrackedCssFiles().filter((filePath) => !importedCssFiles.has(filePath));
190
+ }
191
+ getDependencyEntryFiles() {
192
+ const configuredEntryPaths = this.options?.dependencyEntryPaths;
193
+ if (!configuredEntryPaths || configuredEntryPaths.length === 0) {
194
+ return this.getTrackedCssEntryFiles();
195
+ }
196
+ return configuredEntryPaths.filter(
197
+ (filePath) => this.matchesFileFilter(filePath) && fileSystem.exists(filePath)
198
+ );
199
+ }
118
200
  async handleDependencyChange(bridge) {
119
201
  if (!this.context) {
120
202
  return;
121
203
  }
122
- const cssFiles = this.getTrackedCssFiles();
204
+ const cssFiles = this.getDependencyEntryFiles();
123
205
  if (cssFiles.length === 0) {
124
206
  return;
125
207
  }
@@ -168,6 +250,8 @@ class PostCssProcessorPlugin extends Processor {
168
250
  onCreate: async ({ path: path2, bridge }) => {
169
251
  await this.enqueueWatchTask(async () => {
170
252
  if (this.matchesFileFilter(path2)) {
253
+ this.trackedCssFiles.add(path2);
254
+ this.buildCssDependencyMap();
171
255
  await this.handleCssChange(path2, bridge);
172
256
  return;
173
257
  }
@@ -179,6 +263,8 @@ class PostCssProcessorPlugin extends Processor {
179
263
  if (this.matchesFileFilter(path2)) {
180
264
  this.runtimeCssCache.delete(path2);
181
265
  this.trackedCssFiles.delete(path2);
266
+ this.cssDependencyMap.delete(path2);
267
+ this.buildCssDependencyMap();
182
268
  return;
183
269
  }
184
270
  await this.handleDependencyChange(bridge);
@@ -190,9 +276,28 @@ class PostCssProcessorPlugin extends Processor {
190
276
  }
191
277
  /**
192
278
  * Handles CSS file changes during development.
193
- * Processes the file and broadcasts a css-update event for hot reloading.
279
+ * If the file is an @import dependency, re-processes the parent entry file(s) instead.
280
+ * Broadcasts a css-update event for hot reloading.
194
281
  */
195
282
  async handleCssChange(filePath, bridge, refreshPlugins = true) {
283
+ if (!this.context) return;
284
+ if (!fileSystem.exists(filePath)) return;
285
+ const entryFiles = this.resolveEntryFiles(filePath);
286
+ if (entryFiles.length > 0) {
287
+ logger.debug(`CSS dependency changed: ${filePath}, re-processing ${entryFiles.length} parent(s)`);
288
+ for (const entryFile of entryFiles) {
289
+ this.runtimeCssCache.delete(entryFile);
290
+ await this.processAndBroadcast(entryFile, bridge, refreshPlugins);
291
+ }
292
+ return;
293
+ }
294
+ await this.processAndBroadcast(filePath, bridge, refreshPlugins);
295
+ }
296
+ /**
297
+ * Processes a CSS file and broadcasts a css-update event.
298
+ * Skips broadcast if the processed output hasn't changed.
299
+ */
300
+ async processAndBroadcast(filePath, bridge, refreshPlugins = true) {
196
301
  if (!this.context) return;
197
302
  if (!fileSystem.exists(filePath)) return;
198
303
  try {
@@ -238,10 +343,24 @@ class PostCssProcessorPlugin extends Processor {
238
343
  ];
239
344
  }
240
345
  /**
241
- * Setup the PostCSS processor.
346
+ * Resolves the configured PostCSS plugin list before config build seals the
347
+ * app manifest.
348
+ *
349
+ * @remarks
350
+ * Runtime setup reuses this prepared list and only performs cache prewarming.
242
351
  */
243
- async setup() {
352
+ async prepareBuildContributions() {
353
+ if (this.buildContributionsPrepared) {
354
+ return;
355
+ }
244
356
  await this.collectPostcssPlugins();
357
+ this.buildContributionsPrepared = true;
358
+ }
359
+ /**
360
+ * Prepares build contributions if not already done and prewarms the runtime CSS cache.
361
+ */
362
+ async setup() {
363
+ await this.prepareBuildContributions();
245
364
  await this.prewarmRuntimeCssCache();
246
365
  }
247
366
  /**
@@ -266,7 +385,10 @@ class PostCssProcessorPlugin extends Processor {
266
385
  if (foundConfigPath) {
267
386
  try {
268
387
  logger.debug(`Loading PostCSS config from: ${foundConfigPath}`);
269
- const postcssConfigModule = await import(foundConfigPath);
388
+ const postcssConfigModule = await import(
389
+ /* @vite-ignore */
390
+ foundConfigPath
391
+ );
270
392
  const postcssConfig = postcssConfigModule.default || postcssConfigModule;
271
393
  if (postcssConfig && typeof postcssConfig.pluginFactories === "object" && postcssConfig.pluginFactories !== null) {
272
394
  loadedPluginFactories = postcssConfig.pluginFactories;
@@ -296,14 +418,15 @@ class PostCssProcessorPlugin extends Processor {
296
418
  } else if (loadedPlugins) {
297
419
  this.pluginFactories = void 0;
298
420
  this.postcssPlugins = loadedPlugins;
299
- } else if (this.options?.pluginFactories) {
300
- logger.debug("Using PostCSS plugin factories provided in processor options.");
301
- this.pluginFactories = this.options.pluginFactories;
302
- this.postcssPlugins = this.materializePluginFactories(this.options.pluginFactories);
303
- } else if (this.options?.plugins) {
304
- logger.debug("Using PostCSS plugins provided in processor options.");
305
- this.pluginFactories = void 0;
306
- this.postcssPlugins = Object.values(this.options.plugins);
421
+ } else if (this.options?.pluginFactories || this.options?.plugins) {
422
+ this.pluginFactories = this.options?.pluginFactories;
423
+ if (this.options?.plugins) {
424
+ logger.debug("Using PostCSS plugins provided in processor options.");
425
+ this.postcssPlugins = Object.values(this.options.plugins);
426
+ } else if (this.options?.pluginFactories) {
427
+ logger.debug("Using PostCSS plugin factories provided in processor options.");
428
+ this.postcssPlugins = this.materializePluginFactories(this.options.pluginFactories);
429
+ }
307
430
  } else {
308
431
  logger.warn(
309
432
  "No PostCSS plugins configured. Use a preset like tailwindV3Preset() or tailwindV4Preset(), provide plugins via options, or create a postcss.config file."
@@ -323,14 +446,19 @@ class PostCssProcessorPlugin extends Processor {
323
446
  * @returns Processed CSS
324
447
  */
325
448
  async process(fileAsString, filePath) {
326
- return await PostCssProcessor.processStringOrBuffer(fileAsString, {
449
+ const input = this.options?.transformInput && filePath ? await this.options.transformInput(fileAsString, filePath) : fileAsString;
450
+ return await PostCssProcessor.processStringOrBuffer(input, {
327
451
  filePath,
328
452
  plugins: this.postcssPlugins,
329
453
  transformOutput: this.options?.transformOutput
330
454
  });
331
455
  }
332
456
  processSync(fileAsString, filePath) {
333
- return PostCssProcessor.processStringOrBufferSync(fileAsString, {
457
+ const input = this.options?.transformInput && filePath ? this.options.transformInput(fileAsString, filePath) : fileAsString;
458
+ if (input instanceof Promise) {
459
+ throw new Error("transformInput must be synchronous when used with processSync");
460
+ }
461
+ return PostCssProcessor.processStringOrBufferSync(input, {
334
462
  filePath,
335
463
  plugins: this.postcssPlugins,
336
464
  transformOutput: this.options?.transformOutput
@@ -2,5 +2,5 @@
2
2
  * PostCSS Processor Presets
3
3
  * @module @ecopages/postcss-processor/presets
4
4
  */
5
- export { tailwindV3Preset } from './tailwind-v3';
6
- export { tailwindV4Preset, type TailwindV4PresetOptions } from './tailwind-v4';
5
+ export { tailwindV3Preset } from './tailwind-v3.js';
6
+ export { tailwindV4Preset, type TailwindV4PresetOptions } from './tailwind-v4.js';
@@ -1,5 +1,5 @@
1
- import { tailwindV3Preset } from "./tailwind-v3";
2
- import { tailwindV4Preset } from "./tailwind-v4";
1
+ import { tailwindV3Preset } from "./tailwind-v3.js";
2
+ import { tailwindV4Preset } from "./tailwind-v4.js";
3
3
  export {
4
4
  tailwindV3Preset,
5
5
  tailwindV4Preset
@@ -11,14 +11,18 @@ function tailwindV4Preset(options) {
11
11
  const autoprefixerOptions = browserslistConfig ? {} : {
12
12
  overrideBrowserslist: [">0.3%", "not ie 11", "not dead", "not op_mini all"]
13
13
  };
14
+ const createTailwindPlugin = () => {
15
+ return tailwindcss({ optimize: false });
16
+ };
14
17
  const pluginFactories = {
15
18
  "postcss-import": () => postcssImport(),
16
19
  "postcss-nested": () => postcssNested(),
17
- "@tailwindcss/postcss": () => tailwindcss(),
20
+ "@tailwindcss/postcss": createTailwindPlugin,
18
21
  autoprefixer: () => autoprefixer(autoprefixerOptions),
19
22
  cssnano: () => cssnano()
20
23
  };
21
24
  return {
25
+ dependencyEntryPaths: [referencePath],
22
26
  /**
23
27
  * Instantiate the initial plugin list for the active processor instance.
24
28
  * Fresh instances can later be recreated from `pluginFactories`.
@@ -1,5 +1,5 @@
1
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
2
- import type { CssTransform } from './css-runtime-contract';
1
+ import type { EcoBuildPlugin } from '@ecopages/core/plugins/processor';
2
+ import type { CssTransform } from './css-runtime-contract.js';
3
3
  type CssLoaderOptions = {
4
4
  name: string;
5
5
  filter: RegExp;
@@ -1,4 +1,4 @@
1
- import { getFileAsBuffer } from "../postcss-processor";
1
+ import { getFileAsBuffer } from "../postcss-processor.js";
2
2
  const createCssLoaderPlugin = ({ name, filter, transform }) => ({
3
3
  name,
4
4
  setup(build) {
@@ -1,5 +1,5 @@
1
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
2
- import type { CssTransform } from './css-runtime-contract';
1
+ import type { EcoBuildPlugin } from '@ecopages/core/plugins/processor';
2
+ import type { CssTransform } from './css-runtime-contract.js';
3
3
  type BunCssLoaderOptions = {
4
4
  name: string;
5
5
  filter: RegExp;
@@ -1,4 +1,4 @@
1
- import { getFileAsBuffer } from "../postcss-processor";
1
+ import { getFileAsBuffer } from "../postcss-processor.js";
2
2
  const createBunCssLoaderPlugin = ({ name, filter, transform }) => ({
3
3
  name,
4
4
  setup(build) {
package/CHANGELOG.md DELETED
@@ -1,26 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to `@ecopages/postcss-processor` are documented here.
4
-
5
- > **Note:** Changelog tracking begins at version `0.2.0`. Changes prior to this release are not recorded here but are available in the git history.
6
-
7
- ## [UNRELEASED] — TBD
8
-
9
- ### Bug Fixes
10
-
11
- - Rebuilt tracked stylesheets from fresh PostCSS plugin instances on non-CSS source changes so Tailwind utility generation updates without stale caches or forced reloads.
12
-
13
- ### Features
14
-
15
- - **Runtime CSS loaders** — Added `css-loader-plugin.ts`, `css-loader.bun.ts`, and `css-runtime-contract.ts` to support runtime CSS loading. CSS is now cached at runtime to avoid redundant processing during HMR (`cbaafea4`, `e7653c9b`).
16
- - **`PostcssProcessor` class** — New `postcss-processor.ts` exposes a programmatic API for the processor separate from the plugin DSL.
17
-
18
- ### Refactoring
19
-
20
- - `plugin.ts` significantly overhauled to integrate with the new build adapter and support build dependency graph registration (`e7653c9b`).
21
- - Test suite updated for esbuild adapter and Node runtime compatibility (`31a44458`).
22
- - Removed unused `@types/postcss` dev dependency.
23
-
24
- ### Tests
25
-
26
- - Updated `plugin.test.ts`, `postcss-processor.test.ts`, and `presets.test.ts` for new plugin contract.