@ecopages/postcss-processor 0.2.0-alpha.1 → 0.2.0-alpha.11

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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
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
+ ### Features
10
+
11
+ - Added reusable runtime CSS loading, a public `PostcssProcessor` class, and build-adapter registration for the plugin.
12
+
13
+ ### Bug Fixes
14
+
15
+ - Fixed runtime PostCSS config loading and stylesheet rebuilds for Tailwind-driven template changes.
16
+ - Fixed direct stylesheet processing and preset output so Tailwind v4 preserves injected references and nested BEM selectors.
17
+
18
+ ### Tests
19
+
20
+ - Added processor and preset coverage for the runtime CSS loader and build adapter flow.
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
15
  bunx jsr 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.1",
3
+ "version": "0.2.0-alpha.11",
4
4
  "description": "Postcss processor, transform string or postcss file to css",
5
5
  "keywords": [
6
6
  "postcss",
@@ -8,24 +8,16 @@
8
8
  "css"
9
9
  ],
10
10
  "license": "MIT",
11
- "main": "./src/postcss-processor.ts",
11
+ "main": "./src/postcss-processor.js",
12
12
  "type": "module",
13
- "types": "./src/postcss-processor.ts",
14
- "files": [
15
- "src"
16
- ],
13
+ "types": "./src/postcss-processor.d.ts",
17
14
  "repository": {
18
15
  "type": "git",
19
16
  "url": "https://github.com/ecopages/ecopages.git",
20
17
  "directory": "packages/processors/postcss-processor"
21
18
  },
22
- "scripts": {
23
- "typecheck": "tsc --noEmit",
24
- "release:jsr": "bunx jsr publish"
25
- },
26
19
  "dependencies": {
27
- "@ecopages/core": "workspace:*",
28
- "@ecopages/file-system": "workspace:*",
20
+ "@ecopages/file-system": "0.2.0-alpha.11",
29
21
  "@ecopages/logger": "latest",
30
22
  "autoprefixer": "^10.4.0",
31
23
  "browserslist": "^4.28.1",
@@ -35,6 +27,7 @@
35
27
  "postcss-nested": "^7.0.2"
36
28
  },
37
29
  "peerDependencies": {
30
+ "@ecopages/core": "0.2.0-alpha.11",
38
31
  "@tailwindcss/postcss": ">=4",
39
32
  "tailwindcss": ">=3"
40
33
  },
@@ -46,37 +39,50 @@
46
39
  "optional": true
47
40
  }
48
41
  },
49
- "devDependencies": {
50
- "@tailwindcss/postcss": "^4.1.18",
51
- "@types/bun": "latest",
52
- "@types/postcss-import": "^14",
53
- "postcss-simple-vars": "^7.0.1",
54
- "tailwindcss": "^3.4.19"
55
- },
56
42
  "exports": {
57
43
  ".": {
58
- "default": "./src/index.ts",
59
- "types": "./src/index.ts"
44
+ "default": "./src/index.js",
45
+ "types": "./src/index.d.ts"
60
46
  },
61
47
  "./postcss-processor": {
62
- "default": "./src/postcss-processor.ts",
63
- "types": "./src/postcss-processor.ts"
48
+ "default": "./src/postcss-processor.js",
49
+ "types": "./src/postcss-processor.d.ts"
64
50
  },
65
51
  "./plugin": {
66
- "default": "./src/plugin.ts",
67
- "types": "./src/plugin.ts"
52
+ "default": "./src/plugin.js",
53
+ "types": "./src/plugin.d.ts"
68
54
  },
69
55
  "./presets": {
70
- "default": "./src/presets/index.ts",
71
- "types": "./src/presets/index.ts"
56
+ "default": "./src/presets/index.js",
57
+ "types": "./src/presets/index.d.ts"
72
58
  },
73
59
  "./presets/tailwind-v3": {
74
- "default": "./src/presets/tailwind-v3.ts",
75
- "types": "./src/presets/tailwind-v3.ts"
60
+ "default": "./src/presets/tailwind-v3.js",
61
+ "types": "./src/presets/tailwind-v3.d.ts"
76
62
  },
77
63
  "./presets/tailwind-v4": {
78
- "default": "./src/presets/tailwind-v4.ts",
79
- "types": "./src/presets/tailwind-v4.ts"
64
+ "default": "./src/presets/tailwind-v4.js",
65
+ "types": "./src/presets/tailwind-v4.d.ts"
66
+ },
67
+ "./postcss-processor.ts": {
68
+ "default": "./src/postcss-processor.js",
69
+ "types": "./src/postcss-processor.d.ts"
70
+ },
71
+ "./plugin.ts": {
72
+ "default": "./src/plugin.js",
73
+ "types": "./src/plugin.d.ts"
74
+ },
75
+ "./presets.ts": {
76
+ "default": "./src/presets/index.js",
77
+ "types": "./src/presets/index.d.ts"
78
+ },
79
+ "./presets/tailwind-v3.ts": {
80
+ "default": "./src/presets/tailwind-v3.js",
81
+ "types": "./src/presets/tailwind-v3.d.ts"
82
+ },
83
+ "./presets/tailwind-v4.ts": {
84
+ "default": "./src/presets/tailwind-v4.js",
85
+ "types": "./src/presets/tailwind-v4.d.ts"
80
86
  }
81
87
  }
82
- }
88
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './plugin.js';
2
+ export * from './postcss-processor.js';
package/src/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./plugin.js";
2
+ export * from "./postcss-processor.js";
@@ -0,0 +1,154 @@
1
+ /**
2
+ * PostCssProcessorPlugin
3
+ * @module @ecopages/postcss-processor
4
+ */
5
+ import { Processor, type ProcessorConfig } from '@ecopages/core/plugins/processor';
6
+ import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
7
+ import type postcss from 'postcss';
8
+ /**
9
+ * Record of PostCSS plugins keyed by name
10
+ */
11
+ export type PluginsRecord = Record<string, postcss.AcceptedPlugin>;
12
+ /**
13
+ * Lazily creates PostCSS plugins.
14
+ *
15
+ * This is primarily used in development when a non-CSS file change forces the
16
+ * processor to rebuild tracked stylesheets. Some plugins, including Tailwind,
17
+ * keep internal caches in long-lived plugin instances, so recreating them is
18
+ * required to pick up newly discovered classes.
19
+ */
20
+ export type PluginFactoryRecord = Record<string, () => postcss.AcceptedPlugin>;
21
+ /**
22
+ * Configuration for the PostCSS processor
23
+ */
24
+ export interface PostCssProcessorPluginConfig {
25
+ /**
26
+ * Regex filter to match files to process
27
+ */
28
+ filter?: RegExp;
29
+ /**
30
+ * Function to transform the contents of the file.
31
+ * It can be handy to add a custom header or footer to the file.
32
+ * Useful for injecting Tailwind v4 `@reference` directives.
33
+ * @param contents The contents of the file
34
+ * @param filePath The absolute path to the CSS file being processed
35
+ * @returns The transformed contents
36
+ */
37
+ transformInput?: (contents: string | Buffer, filePath: string) => string | Promise<string>;
38
+ /**
39
+ * Function to transform the output CSS after PostCSS processing.
40
+ * It can be handy to add a custom header or footer to the processed CSS.
41
+ * @param css The processed CSS
42
+ * @returns The transformed CSS
43
+ */
44
+ transformOutput?: (css: string) => Promise<string> | string;
45
+ /**
46
+ * Custom PostCSS plugins to use instead of the default ones
47
+ * @default undefined (uses default plugins)
48
+ */
49
+ plugins?: PluginsRecord;
50
+ /**
51
+ * Factory functions for recreating stateful PostCSS plugins.
52
+ *
53
+ * When provided, Ecopages uses these factories to build a fresh plugin list
54
+ * for dependency-driven stylesheet rebuilds during development.
55
+ */
56
+ pluginFactories?: PluginFactoryRecord;
57
+ }
58
+ /**
59
+ * PostCssProcessorPlugin
60
+ * A Processor for transforming CSS files.
61
+ */
62
+ export declare class PostCssProcessorPlugin extends Processor<PostCssProcessorPluginConfig> {
63
+ static DEFAULT_OPTIONS: Required<Pick<PostCssProcessorPluginConfig, 'filter'>>;
64
+ private buildContributionsPrepared;
65
+ private postcssPlugins;
66
+ private pluginFactories?;
67
+ private readonly runtimeCssCache;
68
+ private readonly trackedCssFiles;
69
+ private watchQueue;
70
+ /**
71
+ * Maps an imported CSS file path → set of tracked CSS entry files that import it.
72
+ * Used to resolve which parent entry files need re-processing when a dependency changes.
73
+ */
74
+ private readonly cssDependencyMap;
75
+ private getCssFilter;
76
+ private resolveProcessedCssPath;
77
+ private readProcessedCssFromDist;
78
+ private persistProcessedCss;
79
+ private prewarmRuntimeCssCache;
80
+ /**
81
+ * Regex to match CSS @import statements and extract the path.
82
+ * Handles: @import './foo.css'; @import "./foo.css"; @import url('./foo.css');
83
+ */
84
+ private static readonly CSS_IMPORT_REGEX;
85
+ /**
86
+ * Builds the CSS dependency map by scanning tracked CSS files for @import directives.
87
+ * Maps each imported file to the set of tracked entry files that import it (directly or transitively).
88
+ */
89
+ private buildCssDependencyMap;
90
+ /**
91
+ * Extracts resolved absolute paths of CSS files imported via @import in the given CSS content.
92
+ * Recursively follows imports to capture transitive dependencies.
93
+ * It skips bare module imports like @import 'tailwindcss'.
94
+ * It recursively follows imports to capture transitive dependencies.
95
+ */
96
+ private extractCssImports;
97
+ /**
98
+ * Resolves a changed CSS file to its parent entry file(s) if it is an @import dependency.
99
+ * Returns an empty array if the file is not an import dependency (i.e., it's an entry file itself).
100
+ */
101
+ private resolveEntryFiles;
102
+ private transformCssSync;
103
+ private transformCssAsync;
104
+ matchesFileFilter(filepath: string): boolean;
105
+ private materializePluginFactories;
106
+ private refreshConfiguredPlugins;
107
+ private enqueueWatchTask;
108
+ private getTrackedCssFiles;
109
+ private handleDependencyChange;
110
+ constructor(config?: Omit<ProcessorConfig<PostCssProcessorPluginConfig>, 'name' | 'description'>);
111
+ /**
112
+ * Handles CSS file changes during development.
113
+ * If the file is an @import dependency, re-processes the parent entry file(s) instead.
114
+ * Broadcasts a css-update event for hot reloading.
115
+ */
116
+ private handleCssChange;
117
+ /**
118
+ * Processes a CSS file and broadcasts a css-update event.
119
+ * Skips broadcast if the processed output hasn't changed.
120
+ */
121
+ private processAndBroadcast;
122
+ get buildPlugins(): EcoBuildPlugin[];
123
+ get plugins(): EcoBuildPlugin[];
124
+ /**
125
+ * Resolves the configured PostCSS plugin list before config build seals the
126
+ * app manifest.
127
+ *
128
+ * @remarks
129
+ * Runtime setup reuses this prepared list and only performs cache prewarming.
130
+ */
131
+ prepareBuildContributions(): Promise<void>;
132
+ /**
133
+ * Prepares build contributions if not already done and prewarms the runtime CSS cache.
134
+ */
135
+ setup(): Promise<void>;
136
+ /**
137
+ * Get the PostCSS plugins from the options or a config file.
138
+ * Searches for postcss.config.{js,cjs,mjs,ts} in the root directory.
139
+ */
140
+ private collectPostcssPlugins;
141
+ /**
142
+ * Process CSS content
143
+ * @param fileAsString CSS content as string
144
+ * @param filePath Optional file path for resolving relative imports
145
+ * @returns Processed CSS
146
+ */
147
+ process(fileAsString: string, filePath?: string): Promise<string>;
148
+ processSync(fileAsString: string, filePath?: string): string;
149
+ /**
150
+ * Teardown the PostCSS processor.
151
+ */
152
+ teardown(): Promise<void>;
153
+ }
154
+ export declare const postcssProcessorPlugin: (config?: PostCssProcessorPluginConfig) => PostCssProcessorPlugin;