@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 +20 -0
- package/README.md +31 -67
- package/package.json +38 -32
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/plugin.d.ts +154 -0
- package/src/plugin.js +469 -0
- package/src/postcss-processor.d.ts +48 -0
- package/src/postcss-processor.js +69 -0
- package/src/presets/{index.ts → index.d.ts} +2 -3
- package/src/presets/index.js +6 -0
- package/src/presets/tailwind-v3.d.ts +34 -0
- package/src/presets/tailwind-v3.js +26 -0
- package/src/presets/tailwind-v4.d.ts +47 -0
- package/src/presets/tailwind-v4.js +57 -0
- package/src/runtime/css-loader-plugin.d.ts +9 -0
- package/src/runtime/css-loader-plugin.js +28 -0
- package/src/runtime/css-loader.bun.d.ts +9 -0
- package/src/runtime/css-loader.bun.js +22 -0
- package/src/runtime/{css-runtime-contract.ts → css-runtime-contract.d.ts} +2 -3
- package/src/runtime/css-runtime-contract.js +0 -0
- package/src/index.ts +0 -2
- package/src/plugin.ts +0 -354
- package/src/postcss-processor.ts +0 -157
- package/src/presets/tailwind-v3.ts +0 -61
- package/src/presets/tailwind-v4.ts +0 -113
- package/src/runtime/css-loader-plugin.ts +0 -37
- package/src/runtime/css-loader.bun.ts +0 -30
- package/src/test/css/base.css +0 -3
- package/src/test/css/correct.css +0 -3
- package/src/test/css/error.css +0 -3
- package/src/test/css/external-plugins.css +0 -13
- package/src/test/css/import.css +0 -4
- package/src/test/plugin.test.ts +0 -74
- package/src/test/postcss-processor.test.ts +0 -106
- package/src/test/presets.test.ts +0 -140
package/src/postcss-processor.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains the PostCSS Processor
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
7
|
-
import { Logger } from '@ecopages/logger';
|
|
8
|
-
import postcss from 'postcss';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* PostCSS Processor Options
|
|
12
|
-
*/
|
|
13
|
-
export type PostCssProcessorOptions = {
|
|
14
|
-
plugins?: postcss.AcceptedPlugin[];
|
|
15
|
-
/**
|
|
16
|
-
* Optional file path for resolving relative imports
|
|
17
|
-
*/
|
|
18
|
-
filePath?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Optional callback to transform the output CSS
|
|
21
|
-
* @param css The processed CSS
|
|
22
|
-
* @returns The transformed CSS
|
|
23
|
-
*/
|
|
24
|
-
transformOutput?: (css: string) => string | Promise<string>;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* ProcessPath
|
|
29
|
-
* @param path string
|
|
30
|
-
* @param options {@link PostCssProcessorOptions}
|
|
31
|
-
* @returns string
|
|
32
|
-
*/
|
|
33
|
-
export type ProcessPath = (path: string, options?: PostCssProcessorOptions) => Promise<string>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* ProcessStringOrBuffer
|
|
37
|
-
* @param contents string | Buffer
|
|
38
|
-
* @param options {@link PostCssProcessorOptions}
|
|
39
|
-
* @returns string
|
|
40
|
-
*/
|
|
41
|
-
export type ProcessStringOrBuffer = (contents: string | Buffer, options?: PostCssProcessorOptions) => Promise<string>;
|
|
42
|
-
|
|
43
|
-
const appLogger = new Logger('[@ecopages/postcss-processor]');
|
|
44
|
-
|
|
45
|
-
export function getFileAsBuffer(path: string): Buffer {
|
|
46
|
-
try {
|
|
47
|
-
if (!existsSync(path)) {
|
|
48
|
-
throw new Error(`File: ${path} not found`);
|
|
49
|
-
}
|
|
50
|
-
return readFileSync(path);
|
|
51
|
-
} catch (error) {
|
|
52
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
53
|
-
throw new Error(`[ecopages] Error reading file: ${path}, ${errorMessage}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const getPlugins = (options?: PostCssProcessorOptions): postcss.AcceptedPlugin[] => {
|
|
58
|
-
if (!options || !options.plugins) return [];
|
|
59
|
-
return Array.isArray(options.plugins) ? options.plugins : Object.values(options.plugins);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* It processes the given path using PostCSS
|
|
64
|
-
* @param path string
|
|
65
|
-
* @param options {@link PostCssProcessorOptions}
|
|
66
|
-
* @returns string
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* ```ts
|
|
70
|
-
* PostCssProcessor.processPath('path/to/file.css').then((processedCss) => {
|
|
71
|
-
* console.log(processedCss);
|
|
72
|
-
* });\n */
|
|
73
|
-
const processPath: ProcessPath = async (path, options) => {
|
|
74
|
-
const contents = getFileAsBuffer(path);
|
|
75
|
-
|
|
76
|
-
return postcss(getPlugins(options))
|
|
77
|
-
.process(contents, { from: path })
|
|
78
|
-
.then((result) => result.css)
|
|
79
|
-
.catch((error) => {
|
|
80
|
-
appLogger.error('Error processing file with PostCssProcessor', error.message);
|
|
81
|
-
return '';
|
|
82
|
-
});
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* It processes the given string or buffer using PostCSS
|
|
87
|
-
* @param contents string | Buffer
|
|
88
|
-
* @param options {@link PostCssProcessorOptions}
|
|
89
|
-
* @returns string
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```ts
|
|
93
|
-
* const css = `body { @apply bg-blue-500; }`;
|
|
94
|
-
*
|
|
95
|
-
* PostCssProcessor.processString(css).then((processedCss) => {
|
|
96
|
-
* console.log(processedCss);
|
|
97
|
-
* });
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
const processStringOrBuffer: ProcessStringOrBuffer = async (contents, options) => {
|
|
101
|
-
if (!contents) return '';
|
|
102
|
-
|
|
103
|
-
return postcss(getPlugins(options))
|
|
104
|
-
.process(contents, { from: options?.filePath })
|
|
105
|
-
.then(async (result) => {
|
|
106
|
-
let css = result.css;
|
|
107
|
-
if (options?.transformOutput) {
|
|
108
|
-
css = await options.transformOutput(css);
|
|
109
|
-
}
|
|
110
|
-
return css;
|
|
111
|
-
})
|
|
112
|
-
.catch((error) => {
|
|
113
|
-
appLogger.error('Error processing string or buffer with PostCssProcessor', error.message);
|
|
114
|
-
return '';
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export type ProcessStringOrBufferSync = (contents: string | Buffer, options?: PostCssProcessorOptions) => string;
|
|
119
|
-
|
|
120
|
-
const processStringOrBufferSync: ProcessStringOrBufferSync = (contents, options) => {
|
|
121
|
-
if (!contents) return '';
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const result = postcss(getPlugins(options)).process(contents, { from: options?.filePath });
|
|
125
|
-
let css = result.css;
|
|
126
|
-
if (options?.transformOutput) {
|
|
127
|
-
const output = options.transformOutput(css);
|
|
128
|
-
if (output instanceof Promise) {
|
|
129
|
-
throw new Error('transformOutput must be synchronous when used with processStringOrBufferSync');
|
|
130
|
-
}
|
|
131
|
-
css = output;
|
|
132
|
-
}
|
|
133
|
-
return css;
|
|
134
|
-
} catch (error) {
|
|
135
|
-
if (error instanceof Error && error.message.includes('transformOutput must be synchronous')) {
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
appLogger.error('Error processing string or buffer with PostCssProcessor', (error as Error).message);
|
|
139
|
-
return '';
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* PostCSS Processor
|
|
145
|
-
* - {@link processPath} : It processes the given path using PostCSS
|
|
146
|
-
* - {@link processStringOrBuffer}: It processes the given string or buffer using PostCSS
|
|
147
|
-
* - {@link processStringOrBufferSync}: It processes the given string or buffer synchronously using PostCSS (requires all plugins to be sync)
|
|
148
|
-
*/
|
|
149
|
-
export const PostCssProcessor: {
|
|
150
|
-
processPath: ProcessPath;
|
|
151
|
-
processStringOrBuffer: ProcessStringOrBuffer;
|
|
152
|
-
processStringOrBufferSync: ProcessStringOrBufferSync;
|
|
153
|
-
} = {
|
|
154
|
-
processPath,
|
|
155
|
-
processStringOrBuffer,
|
|
156
|
-
processStringOrBufferSync,
|
|
157
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tailwind CSS v3 Preset for PostCSS Processor
|
|
3
|
-
* @module @ecopages/postcss-processor/presets/tailwind-v3
|
|
4
|
-
*
|
|
5
|
-
* Requires: tailwindcss, postcss-import, autoprefixer, cssnano
|
|
6
|
-
* Install: bun add tailwindcss postcss-import autoprefixer cssnano
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import autoprefixer from 'autoprefixer';
|
|
10
|
-
import browserslist from 'browserslist';
|
|
11
|
-
import cssnano from 'cssnano';
|
|
12
|
-
import type postcss from 'postcss';
|
|
13
|
-
import postcssImport from 'postcss-import';
|
|
14
|
-
import tailwindcss from 'tailwindcss';
|
|
15
|
-
import tailwindcssNesting from 'tailwindcss/nesting/index.js';
|
|
16
|
-
import type { PostCssProcessorPluginConfig } from '../plugin.ts';
|
|
17
|
-
|
|
18
|
-
type PluginsRecord = Record<string, postcss.AcceptedPlugin>;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Creates a PostCSS processor config preset for Tailwind CSS v3.
|
|
22
|
-
*
|
|
23
|
-
* Features:
|
|
24
|
-
* - Uses classic Tailwind v3 plugin stack
|
|
25
|
-
* - Includes postcss-import, tailwindcss/nesting, tailwindcss, autoprefixer, cssnano
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```typescript
|
|
29
|
-
* import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
|
|
30
|
-
* import { tailwindV3Preset } from '@ecopages/postcss-processor/presets';
|
|
31
|
-
*
|
|
32
|
-
* // Basic usage
|
|
33
|
-
* postcssProcessorPlugin(tailwindV3Preset())
|
|
34
|
-
*
|
|
35
|
-
* // Extend with additional plugins
|
|
36
|
-
* const preset = tailwindV3Preset();
|
|
37
|
-
* postcssProcessorPlugin({
|
|
38
|
-
* ...preset,
|
|
39
|
-
* plugins: { ...preset.plugins, myPlugin: myPlugin() },
|
|
40
|
-
* })
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export function tailwindV3Preset(): PostCssProcessorPluginConfig {
|
|
44
|
-
// Check if browserslist config exists
|
|
45
|
-
const browserslistConfig = browserslist.loadConfig({ path: process.cwd() });
|
|
46
|
-
const autoprefixerOptions = browserslistConfig
|
|
47
|
-
? {}
|
|
48
|
-
: {
|
|
49
|
-
overrideBrowserslist: ['>0.3%', 'not ie 11', 'not dead', 'not op_mini all'],
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const plugins: PluginsRecord = {
|
|
53
|
-
'postcss-import': postcssImport(),
|
|
54
|
-
'tailwindcss/nesting': tailwindcssNesting(),
|
|
55
|
-
tailwindcss: tailwindcss(),
|
|
56
|
-
autoprefixer: autoprefixer(autoprefixerOptions),
|
|
57
|
-
cssnano: cssnano(),
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
return { plugins };
|
|
61
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tailwind CSS v4 Preset for PostCSS Processor
|
|
3
|
-
* @module @ecopages/postcss-processor/presets/tailwind-v4
|
|
4
|
-
*
|
|
5
|
-
* Requires: @tailwindcss/postcss, cssnano
|
|
6
|
-
* Install: bun add @tailwindcss/postcss cssnano
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import tailwindcss from '@tailwindcss/postcss';
|
|
10
|
-
import autoprefixer from 'autoprefixer';
|
|
11
|
-
import browserslist from 'browserslist';
|
|
12
|
-
import cssnano from 'cssnano';
|
|
13
|
-
import path from 'node:path';
|
|
14
|
-
import postcssImport from 'postcss-import';
|
|
15
|
-
import postcssNested from 'postcss-nested';
|
|
16
|
-
import type { PostCssProcessorPluginConfig } from '../plugin.ts';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Options for Tailwind v4 preset
|
|
20
|
-
*/
|
|
21
|
-
export interface TailwindV4PresetOptions {
|
|
22
|
-
/**
|
|
23
|
-
* Absolute path to the main Tailwind CSS file containing `@import "tailwindcss"`.
|
|
24
|
-
* Used to calculate relative @reference paths for CSS files using @apply.
|
|
25
|
-
*/
|
|
26
|
-
referencePath: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Creates a PostCSS processor config preset for Tailwind CSS v4.
|
|
31
|
-
*
|
|
32
|
-
* Features:
|
|
33
|
-
* - Uses `@tailwindcss/postcss` plugin (v4)
|
|
34
|
-
* - Automatically injects `@reference` headers for `@apply` support
|
|
35
|
-
* - Includes cssnano for CSS minification
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* import { postcssProcessorPlugin } from '@ecopages/postcss-processor';
|
|
40
|
-
* import { tailwindV4Preset } from '@ecopages/postcss-processor/presets';
|
|
41
|
-
*
|
|
42
|
-
* // Basic usage
|
|
43
|
-
* postcssProcessorPlugin(tailwindV4Preset({
|
|
44
|
-
* referencePath: path.resolve(import.meta.dir, 'src/styles/tailwind.css'),
|
|
45
|
-
* }))
|
|
46
|
-
*
|
|
47
|
-
* // Extend with additional plugins
|
|
48
|
-
* const preset = tailwindV4Preset({ referencePath });
|
|
49
|
-
* postcssProcessorPlugin({
|
|
50
|
-
* ...preset,
|
|
51
|
-
* plugins: { ...preset.plugins, myPlugin: myPlugin() },
|
|
52
|
-
* })
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export function tailwindV4Preset(options: TailwindV4PresetOptions): PostCssProcessorPluginConfig {
|
|
56
|
-
const { referencePath } = options;
|
|
57
|
-
|
|
58
|
-
// Check if browserslist config exists
|
|
59
|
-
const browserslistConfig = browserslist.loadConfig({ path: process.cwd() });
|
|
60
|
-
const autoprefixerOptions = browserslistConfig
|
|
61
|
-
? {}
|
|
62
|
-
: {
|
|
63
|
-
overrideBrowserslist: ['>0.3%', 'not ie 11', 'not dead', 'not op_mini all'],
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
plugins: {
|
|
68
|
-
'postcss-import': postcssImport(),
|
|
69
|
-
'postcss-nested': postcssNested(),
|
|
70
|
-
'@tailwindcss/postcss': tailwindcss(),
|
|
71
|
-
autoprefixer: autoprefixer(autoprefixerOptions),
|
|
72
|
-
cssnano: cssnano(),
|
|
73
|
-
},
|
|
74
|
-
transformInput: async (contents: string | Buffer, filePath: string): Promise<string> => {
|
|
75
|
-
const css = contents instanceof Buffer ? contents.toString('utf-8') : (contents as string);
|
|
76
|
-
const normalizedFilePath = path.resolve(filePath);
|
|
77
|
-
const normalizedReferencePath = path.resolve(referencePath);
|
|
78
|
-
|
|
79
|
-
/** Skip transformation for the main tailwind entry file */
|
|
80
|
-
if (normalizedFilePath === normalizedReferencePath) {
|
|
81
|
-
return css;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Skip if file already has an @reference directive */
|
|
85
|
-
if (/^\s*@reference\s+/m.test(css)) {
|
|
86
|
-
return css;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const relativePath = path.relative(path.dirname(filePath), referencePath);
|
|
90
|
-
|
|
91
|
-
/** Skip if file already imports the referencePath */
|
|
92
|
-
if (css.includes(`@import '${relativePath}'`) || css.includes(`@import "${relativePath}"`)) {
|
|
93
|
-
return css;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Replace `@import 'tailwindcss'` with an import to the referencePath.
|
|
98
|
-
* This ensures custom theme variables are available for @apply directives.
|
|
99
|
-
*/
|
|
100
|
-
const tailwindImportPattern = /^@import\s+['"]tailwindcss(?:\/[^'"]*)?['"];?\s*$/m;
|
|
101
|
-
if (tailwindImportPattern.test(css)) {
|
|
102
|
-
return css.replace(tailwindImportPattern, `@import '${relativePath}';`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** If file uses @apply but has no tailwind import, add @reference */
|
|
106
|
-
if (css.includes('@apply')) {
|
|
107
|
-
return `@reference "${relativePath}";\n\n${css}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return css;
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
2
|
-
|
|
3
|
-
import { getFileAsBuffer } from '../postcss-processor';
|
|
4
|
-
import type { CssTransform } from './css-runtime-contract';
|
|
5
|
-
|
|
6
|
-
type CssLoaderOptions = {
|
|
7
|
-
name: string;
|
|
8
|
-
filter: RegExp;
|
|
9
|
-
transform: CssTransform;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const createCssLoaderPlugin = ({ name, filter, transform }: CssLoaderOptions): EcoBuildPlugin => ({
|
|
13
|
-
name,
|
|
14
|
-
setup(build) {
|
|
15
|
-
build.onLoad({ filter }, (args) => {
|
|
16
|
-
const rawFile = getFileAsBuffer(args.path);
|
|
17
|
-
const css = transform({
|
|
18
|
-
contents: rawFile,
|
|
19
|
-
filePath: args.path,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (css instanceof Promise) {
|
|
23
|
-
return css.then((resolved) => ({
|
|
24
|
-
exports: { default: resolved },
|
|
25
|
-
loader: 'object' as const,
|
|
26
|
-
}));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
exports: {
|
|
31
|
-
default: css as string,
|
|
32
|
-
},
|
|
33
|
-
loader: 'object' as const,
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
},
|
|
37
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
|
|
2
|
-
|
|
3
|
-
import { getFileAsBuffer } from '../postcss-processor';
|
|
4
|
-
import type { CssTransform } from './css-runtime-contract';
|
|
5
|
-
|
|
6
|
-
type BunCssLoaderOptions = {
|
|
7
|
-
name: string;
|
|
8
|
-
filter: RegExp;
|
|
9
|
-
transform: CssTransform;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const createBunCssLoaderPlugin = ({ name, filter, transform }: BunCssLoaderOptions): EcoBuildPlugin => ({
|
|
13
|
-
name,
|
|
14
|
-
setup(build) {
|
|
15
|
-
build.onLoad({ filter }, async (args) => {
|
|
16
|
-
const rawFile = await getFileAsBuffer(args.path);
|
|
17
|
-
const css = await transform({
|
|
18
|
-
contents: rawFile,
|
|
19
|
-
filePath: args.path,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
exports: {
|
|
24
|
-
default: css,
|
|
25
|
-
},
|
|
26
|
-
loader: 'object',
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
},
|
|
30
|
-
});
|
package/src/test/css/base.css
DELETED
package/src/test/css/correct.css
DELETED
package/src/test/css/error.css
DELETED
package/src/test/css/import.css
DELETED
package/src/test/plugin.test.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, afterAll, beforeAll, vi } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import { PostCssProcessorPlugin } from '../plugin';
|
|
5
|
-
import type { ClientBridge } from '@ecopages/core/adapters/bun/client-bridge';
|
|
6
|
-
import { ConfigBuilder } from '@ecopages/core/config-builder';
|
|
7
|
-
|
|
8
|
-
const TMP_DIR = path.join(__dirname, 'tmp_test_hmr');
|
|
9
|
-
const SRC_DIR = path.join(TMP_DIR, 'src');
|
|
10
|
-
const DIST_DIR = path.join(TMP_DIR, 'dist');
|
|
11
|
-
|
|
12
|
-
describe('PostCssProcessorPlugin HMR', () => {
|
|
13
|
-
beforeAll(() => {
|
|
14
|
-
if (fs.existsSync(TMP_DIR)) {
|
|
15
|
-
fs.rmSync(TMP_DIR, { recursive: true, force: true });
|
|
16
|
-
}
|
|
17
|
-
fs.mkdirSync(SRC_DIR, { recursive: true });
|
|
18
|
-
fs.mkdirSync(DIST_DIR, { recursive: true });
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterAll(() => {
|
|
22
|
-
fs.rmSync(TMP_DIR, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('handleCssChange should apply transformInput', async () => {
|
|
26
|
-
const cssFile = path.join(SRC_DIR, 'style.css');
|
|
27
|
-
fs.writeFileSync(cssFile, '.foo { color: red; }');
|
|
28
|
-
|
|
29
|
-
const plugin = new PostCssProcessorPlugin({
|
|
30
|
-
options: {
|
|
31
|
-
filter: /\.css$/,
|
|
32
|
-
transformInput: async (content) => {
|
|
33
|
-
return `/* prefix */\n${content}`;
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const config = await new ConfigBuilder()
|
|
39
|
-
.setRootDir(TMP_DIR)
|
|
40
|
-
.setSrcDir('src')
|
|
41
|
-
.setDistDir('dist')
|
|
42
|
-
.setBaseUrl('http://localhost:3000')
|
|
43
|
-
.build();
|
|
44
|
-
|
|
45
|
-
plugin.setContext(config);
|
|
46
|
-
|
|
47
|
-
const Bridge = {
|
|
48
|
-
cssUpdate: () => {},
|
|
49
|
-
error: (msg: string) => {
|
|
50
|
-
throw new Error(msg);
|
|
51
|
-
},
|
|
52
|
-
reload: () => {},
|
|
53
|
-
} as unknown as ClientBridge;
|
|
54
|
-
|
|
55
|
-
const bridgeSpy = vi.spyOn(Bridge, 'cssUpdate');
|
|
56
|
-
|
|
57
|
-
const watchConfig = plugin.getWatchConfig();
|
|
58
|
-
if (watchConfig && watchConfig.onChange) {
|
|
59
|
-
await watchConfig.onChange({ path: cssFile, bridge: Bridge });
|
|
60
|
-
} else {
|
|
61
|
-
throw new Error('Plugin does not have watch handler');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const outputFile = path.join(DIST_DIR, 'assets', 'style.css');
|
|
65
|
-
expect(fs.existsSync(outputFile)).toBe(true);
|
|
66
|
-
|
|
67
|
-
const outputContent = fs.readFileSync(outputFile, 'utf-8');
|
|
68
|
-
expect(outputContent).toContain('/* prefix */');
|
|
69
|
-
expect(outputContent).toContain('.foo { color: red; }');
|
|
70
|
-
|
|
71
|
-
expect(bridgeSpy).toHaveBeenCalled();
|
|
72
|
-
expect(bridgeSpy).toHaveBeenCalledWith(cssFile);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import postCssSimpleVars from 'postcss-simple-vars';
|
|
4
|
-
import { PostCssProcessor } from '../postcss-processor';
|
|
5
|
-
import { tailwindV3Preset } from '../presets/tailwind-v3';
|
|
6
|
-
|
|
7
|
-
describe('PostCssProcessor', () => {
|
|
8
|
-
test('processPath should return the processed CSS', async () => {
|
|
9
|
-
const filePath = path.resolve(__dirname, './css/correct.css');
|
|
10
|
-
const expected = '.test{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}';
|
|
11
|
-
const { plugins } = tailwindV3Preset();
|
|
12
|
-
const result = await PostCssProcessor.processPath(filePath, { plugins: plugins ? Object.values(plugins) : [] });
|
|
13
|
-
expect(result).toEqual(expected);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('processPath should return an empty string when an error occurs during css conversion', async () => {
|
|
17
|
-
const filePath = path.resolve(__dirname, './css/error.css');
|
|
18
|
-
const expected = '';
|
|
19
|
-
const { plugins } = tailwindV3Preset();
|
|
20
|
-
const result = await PostCssProcessor.processPath(filePath, { plugins: plugins ? Object.values(plugins) : [] });
|
|
21
|
-
expect(result).toEqual(expected);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('processPath should throw when the file does not exist', async () => {
|
|
25
|
-
const filePath = 'fake-path.css';
|
|
26
|
-
await expect(PostCssProcessor.processPath(filePath)).rejects.toThrow();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('processPath should use the custom plugins', async () => {
|
|
30
|
-
const filePath = path.resolve(__dirname, './css/external-plugins.css');
|
|
31
|
-
const expected = '.menu_link{background:#056ef0;width:200px}.menu{margin-top:10px;width:800px}';
|
|
32
|
-
const result = await PostCssProcessor.processPath(filePath, {
|
|
33
|
-
plugins: [...Object.values(tailwindV3Preset().plugins ?? {}), postCssSimpleVars()],
|
|
34
|
-
});
|
|
35
|
-
expect(result).toEqual(expected);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('processStringOrBuffer should return the processed CSS', async () => {
|
|
39
|
-
const string = 'body { @apply bg-white; }';
|
|
40
|
-
const expected = 'body{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}';
|
|
41
|
-
const { plugins } = tailwindV3Preset();
|
|
42
|
-
const result = await PostCssProcessor.processStringOrBuffer(string, {
|
|
43
|
-
plugins: plugins ? Object.values(plugins) : [],
|
|
44
|
-
});
|
|
45
|
-
expect(result).toEqual(expected);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('processStringOrBuffer should return an empty string when an error occurs during css conversion', async () => {
|
|
49
|
-
const string = 'body { @apply bg-whites; }';
|
|
50
|
-
const expected = '';
|
|
51
|
-
const { plugins } = tailwindV3Preset();
|
|
52
|
-
const result = await PostCssProcessor.processStringOrBuffer(string, {
|
|
53
|
-
plugins: plugins ? Object.values(plugins) : [],
|
|
54
|
-
});
|
|
55
|
-
expect(result).toEqual(expected);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('processStringOrBuffer should return an empty string when the input is empty', async () => {
|
|
59
|
-
const string = '';
|
|
60
|
-
const expected = '';
|
|
61
|
-
const result = await PostCssProcessor.processStringOrBuffer(string);
|
|
62
|
-
expect(result).toEqual(expected);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('processStringOrBuffer should use the custom plugins', async () => {
|
|
66
|
-
const string = '$blue: #056ef0; body { background: $blue; }';
|
|
67
|
-
const expected = 'body{background:#056ef0}';
|
|
68
|
-
const result = await PostCssProcessor.processStringOrBuffer(string, {
|
|
69
|
-
plugins: [...Object.values(tailwindV3Preset().plugins ?? {}), postCssSimpleVars()],
|
|
70
|
-
});
|
|
71
|
-
expect(result).toEqual(expected);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('processStringOrBuffer should use the transformOutput', async () => {
|
|
75
|
-
const string = 'body { background: #056ef0; }';
|
|
76
|
-
const expected = '@reference "../app.css";\nbody{background:#056ef0}';
|
|
77
|
-
const { plugins } = tailwindV3Preset();
|
|
78
|
-
const result = await PostCssProcessor.processStringOrBuffer(string, {
|
|
79
|
-
plugins: plugins?.cssnano ? [plugins.cssnano] : [],
|
|
80
|
-
transformOutput: (css) => {
|
|
81
|
-
return `@reference "../app.css";\n${css}`;
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
expect(result).toEqual(expected);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('processPath should resolve @import', async () => {
|
|
88
|
-
const filePath = path.resolve(__dirname, './css/import.css');
|
|
89
|
-
const expected = '.base{color:red}.main{background:blue}';
|
|
90
|
-
const { plugins } = tailwindV3Preset();
|
|
91
|
-
const result = await PostCssProcessor.processPath(filePath, { plugins: plugins ? Object.values(plugins) : [] });
|
|
92
|
-
expect(result).toEqual(expected);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('processStringOrBuffer should resolve @import', async () => {
|
|
96
|
-
const string = '@import "base.css"; .main { background: blue; }';
|
|
97
|
-
const filePath = path.resolve(__dirname, './css/import.css');
|
|
98
|
-
const expected = '.base{color:red}.main{background:blue}';
|
|
99
|
-
const { plugins } = tailwindV3Preset();
|
|
100
|
-
const result = await PostCssProcessor.processStringOrBuffer(string, {
|
|
101
|
-
filePath,
|
|
102
|
-
plugins: plugins ? Object.values(plugins) : [],
|
|
103
|
-
});
|
|
104
|
-
expect(result).toEqual(expected);
|
|
105
|
-
});
|
|
106
|
-
});
|