@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 +32 -68
- package/package.json +9 -9
- package/src/index.d.ts +2 -2
- package/src/index.js +2 -2
- package/src/plugin.d.ts +54 -4
- package/src/plugin.js +145 -17
- package/src/presets/index.d.ts +2 -2
- package/src/presets/index.js +2 -2
- package/src/presets/tailwind-v4.js +5 -1
- package/src/runtime/css-loader-plugin.d.ts +2 -2
- package/src/runtime/css-loader-plugin.js +1 -1
- package/src/runtime/css-loader.bun.d.ts +2 -2
- package/src/runtime/css-loader.bun.js +1 -1
- package/CHANGELOG.md +0 -26
- package/src/index.ts +0 -2
- package/src/plugin.ts +0 -489
- package/src/postcss-processor.ts +0 -157
- package/src/presets/index.ts +0 -7
- package/src/presets/tailwind-v3.ts +0 -72
- package/src/presets/tailwind-v4.ts +0 -122
- package/src/runtime/css-loader-plugin.ts +0 -37
- package/src/runtime/css-loader.bun.ts +0 -30
- package/src/runtime/css-runtime-contract.ts +0 -6
package/README.md
CHANGED
|
@@ -1,56 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ecopages/postcss-processor
|
|
2
2
|
|
|
3
|
-
|
|
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**:
|
|
8
|
-
- **Tailwind Presets**:
|
|
9
|
-
- **Automatic Configuration**: Detects `postcss.config.{js,ts,etc}
|
|
10
|
-
- **
|
|
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
|
-
##
|
|
12
|
+
## Installation
|
|
14
13
|
|
|
15
14
|
```bash
|
|
16
|
-
|
|
15
|
+
bun add @ecopages/postcss-processor
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
## Usage
|
|
18
|
+
## Usage
|
|
20
19
|
|
|
21
|
-
Integrate the processor into your `eco.config.ts` using
|
|
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
|
-
|
|
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.
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
54
|
+
import { tailwindV3Preset } from '@ecopages/postcss-processor/presets/tailwind-v3';
|
|
55
|
+
|
|
56
|
+
const config = await new ConfigBuilder().setProcessors([postcssProcessorPlugin(tailwindV3Preset())]).build();
|
|
88
57
|
|
|
89
|
-
|
|
90
|
-
plugins: {
|
|
91
|
-
'my-plugin': myPlugin(),
|
|
92
|
-
},
|
|
93
|
-
});
|
|
58
|
+
export default config;
|
|
94
59
|
```
|
|
95
60
|
|
|
96
|
-
|
|
61
|
+
## Custom Configuration
|
|
97
62
|
|
|
98
|
-
|
|
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
|
-
|
|
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
|
|
88
|
+
## Standalone Processing
|
|
125
89
|
|
|
126
|
-
You can use the
|
|
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.
|
|
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/
|
|
21
|
-
"@ecopages/
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"postcss": "^
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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(
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/src/presets/index.d.ts
CHANGED
|
@@ -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';
|
package/src/presets/index.js
CHANGED
|
@@ -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":
|
|
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/
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import type { EcoBuildPlugin } from '@ecopages/core/
|
|
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;
|
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.
|