@empjs/plugin-lightningcss 1.0.0
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/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/loader.cjs +57 -0
- package/dist/loader.d.ts +9 -0
- package/dist/loader.js +30 -0
- package/dist/minimizer.d.ts +10 -0
- package/dist/minimizer.js +62 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.js +40 -0
- package/dist/postcss-config.d.ts +86 -0
- package/dist/postcss-config.js +457 -0
- package/dist/px-to-viewport.d.ts +17 -0
- package/dist/px-to-viewport.js +49 -0
- package/package.json +50 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/loader.cjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/loader.ts
|
|
21
|
+
var loader_exports = {};
|
|
22
|
+
__export(loader_exports, {
|
|
23
|
+
default: () => loader_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(loader_exports);
|
|
26
|
+
var import_lightningcss = require("lightningcss");
|
|
27
|
+
var import_node_buffer = require("node:buffer");
|
|
28
|
+
var LOADER_NAME = "lightningcss-loader";
|
|
29
|
+
async function LightningCSSLoader(source, prevMap) {
|
|
30
|
+
const done = this.async();
|
|
31
|
+
const options = this.getOptions();
|
|
32
|
+
const { implementation, targets, ...opts } = options;
|
|
33
|
+
if (implementation && typeof implementation.transform !== "function") {
|
|
34
|
+
done(
|
|
35
|
+
new TypeError(
|
|
36
|
+
`[${LOADER_NAME}]: options.implementation.transform must be an 'lightningcss' transform function. Received ${typeof implementation.transform}`
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const transform = implementation?.transform ?? import_lightningcss.transform;
|
|
42
|
+
try {
|
|
43
|
+
const { code, map } = transform({
|
|
44
|
+
filename: this.resourcePath,
|
|
45
|
+
code: import_node_buffer.Buffer.from(source),
|
|
46
|
+
sourceMap: this.sourceMap,
|
|
47
|
+
targets,
|
|
48
|
+
inputSourceMap: this.sourceMap && prevMap ? JSON.stringify(prevMap) : void 0,
|
|
49
|
+
...opts
|
|
50
|
+
});
|
|
51
|
+
const codeAsString = code.toString();
|
|
52
|
+
done(null, codeAsString, map && JSON.parse(map.toString()));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
done(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var loader_default = LightningCSSLoader;
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LoaderContext } from '@rspack/core';
|
|
2
|
+
import type { CustomAtRules, TransformOptions } from 'lightningcss';
|
|
3
|
+
export type LightningCSSTransformOptions = Omit<TransformOptions<CustomAtRules>, 'filename' | 'code' | 'inputSourceMap'>;
|
|
4
|
+
type Implementation = unknown;
|
|
5
|
+
export type LightningCSSLoaderOptions = LightningCSSTransformOptions & {
|
|
6
|
+
implementation?: Implementation;
|
|
7
|
+
};
|
|
8
|
+
declare function LightningCSSLoader(this: LoaderContext<LightningCSSLoaderOptions>, source: string, prevMap?: Record<string, any>): Promise<void>;
|
|
9
|
+
export default LightningCSSLoader;
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { transform as _transform } from 'lightningcss';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
const LOADER_NAME = 'lightningcss-loader';
|
|
4
|
+
async function LightningCSSLoader(source, prevMap) {
|
|
5
|
+
const done = this.async();
|
|
6
|
+
const options = this.getOptions();
|
|
7
|
+
// console.log('options', options)
|
|
8
|
+
const { implementation, targets, ...opts } = options;
|
|
9
|
+
if (implementation && typeof implementation.transform !== 'function') {
|
|
10
|
+
done(new TypeError(`[${LOADER_NAME}]: options.implementation.transform must be an 'lightningcss' transform function. Received ${typeof implementation.transform}`));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const transform = implementation?.transform ?? _transform;
|
|
14
|
+
try {
|
|
15
|
+
const { code, map } = transform({
|
|
16
|
+
filename: this.resourcePath,
|
|
17
|
+
code: Buffer.from(source),
|
|
18
|
+
sourceMap: this.sourceMap,
|
|
19
|
+
targets,
|
|
20
|
+
inputSourceMap: this.sourceMap && prevMap ? JSON.stringify(prevMap) : undefined,
|
|
21
|
+
...opts,
|
|
22
|
+
});
|
|
23
|
+
const codeAsString = code.toString();
|
|
24
|
+
done(null, codeAsString, map && JSON.parse(map.toString()));
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
done(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export default LightningCSSLoader;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Compiler } from '@rspack/core';
|
|
2
|
+
type LightningCSSMinifyPluginOptions = any;
|
|
3
|
+
export declare class LightningCSSMinifyPlugin {
|
|
4
|
+
private readonly options;
|
|
5
|
+
private readonly transform;
|
|
6
|
+
constructor(opts?: LightningCSSMinifyPluginOptions);
|
|
7
|
+
apply(compiler: Compiler): void;
|
|
8
|
+
private transformAssets;
|
|
9
|
+
}
|
|
10
|
+
export default LightningCSSMinifyPlugin;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { transform as _transform } from 'lightningcss';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
const PLUGIN_NAME = 'lightningcss-minify-plugin';
|
|
4
|
+
//
|
|
5
|
+
const CSS_REGEX = /\.css(?:\?.*)?$/i;
|
|
6
|
+
export class LightningCSSMinifyPlugin {
|
|
7
|
+
options;
|
|
8
|
+
transform;
|
|
9
|
+
constructor(opts = {}) {
|
|
10
|
+
const { implementation } = opts;
|
|
11
|
+
if (implementation && typeof implementation.transform !== 'function') {
|
|
12
|
+
throw new TypeError(`[${PLUGIN_NAME}]: implementation.transform must be an 'lightningcss' transform function. Received ${typeof implementation.transform}`);
|
|
13
|
+
}
|
|
14
|
+
this.transform = implementation?.transform ?? _transform;
|
|
15
|
+
this.options = opts;
|
|
16
|
+
}
|
|
17
|
+
apply(compiler) {
|
|
18
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
|
|
19
|
+
compilation.hooks.processAssets.tapPromise({
|
|
20
|
+
name: PLUGIN_NAME,
|
|
21
|
+
stage: compilation?.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
|
|
22
|
+
}, async () => await this.transformAssets(compilation));
|
|
23
|
+
compilation.hooks.statsPrinter.tap(PLUGIN_NAME, statsPrinter => {
|
|
24
|
+
statsPrinter.hooks.print
|
|
25
|
+
.for('asset.info.minimized')
|
|
26
|
+
.tap(PLUGIN_NAME, (minimized, { green, formatFlag }) => minimized && green && formatFlag ? green(formatFlag('minimized')) : '');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async transformAssets(compilation) {
|
|
31
|
+
const { options: { devtool }, webpack: { sources: { SourceMapSource, RawSource }, }, } = compilation.compiler;
|
|
32
|
+
const sourcemap = this.options.sourceMap === undefined
|
|
33
|
+
? (devtool && devtool.includes('source-map'))
|
|
34
|
+
: this.options.sourceMap;
|
|
35
|
+
const { targets: userTargets, ...transformOptions } = this.options;
|
|
36
|
+
const assets = compilation.getAssets().filter(asset =>
|
|
37
|
+
// Filter out already minimized
|
|
38
|
+
!asset.info.minimized &&
|
|
39
|
+
// Filter out by file type
|
|
40
|
+
CSS_REGEX.test(asset.name));
|
|
41
|
+
await Promise.all(assets.map(async (asset) => {
|
|
42
|
+
const { source, map } = asset.source.sourceAndMap();
|
|
43
|
+
const sourceAsString = source.toString();
|
|
44
|
+
const code = typeof source === 'string' ? Buffer.from(source) : source;
|
|
45
|
+
const result = this.transform({
|
|
46
|
+
filename: asset.name,
|
|
47
|
+
code,
|
|
48
|
+
minify: true,
|
|
49
|
+
sourceMap: sourcemap,
|
|
50
|
+
...transformOptions,
|
|
51
|
+
});
|
|
52
|
+
const codeString = result.code.toString();
|
|
53
|
+
compilation.updateAsset(asset.name, sourcemap
|
|
54
|
+
? new SourceMapSource(codeString, asset.name, JSON.parse(result.map.toString()), sourceAsString, map, true)
|
|
55
|
+
: new RawSource(codeString), {
|
|
56
|
+
...asset.info,
|
|
57
|
+
minimized: true,
|
|
58
|
+
});
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export default LightningCSSMinifyPlugin;
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GlobalStore } from '@empjs/cli';
|
|
2
|
+
import type { TransformAttributeOptions } from 'lightningcss';
|
|
3
|
+
export type PluginLightningCssType = {
|
|
4
|
+
transform?: boolean | TransformAttributeOptions;
|
|
5
|
+
minify?: boolean | unknown;
|
|
6
|
+
};
|
|
7
|
+
declare const empLightningcssPlugin: (o?: PluginLightningCssType) => {
|
|
8
|
+
name: string;
|
|
9
|
+
rsConfig(store: GlobalStore): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
export default empLightningcssPlugin;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { browserslistToTargets } from 'lightningcss';
|
|
2
|
+
import browserslist from 'browserslist';
|
|
3
|
+
const uselightningcssLoader = async (store, o) => {
|
|
4
|
+
if (o?.transform === false)
|
|
5
|
+
return;
|
|
6
|
+
const { importResolve, chain } = store;
|
|
7
|
+
const ruleMap = ['sass', 'less', 'css'];
|
|
8
|
+
//
|
|
9
|
+
const transform = o?.transform || {};
|
|
10
|
+
const targets = browserslistToTargets(browserslist('>= 0.25%'));
|
|
11
|
+
//
|
|
12
|
+
const loaderOptions = { ...transform, targets };
|
|
13
|
+
for (const ruleName of ruleMap) {
|
|
14
|
+
const rule = chain.module.rule(ruleName);
|
|
15
|
+
const useLightningcss = rule
|
|
16
|
+
.use('lightningcss')
|
|
17
|
+
.loader(importResolve('./loader.cjs', import.meta.url))
|
|
18
|
+
.options(loaderOptions);
|
|
19
|
+
useLightningcss.before('postcss');
|
|
20
|
+
rule.uses.delete('postcss');
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const uselightningcssMinify = async (store, o) => {
|
|
24
|
+
if (o?.minify === false)
|
|
25
|
+
return;
|
|
26
|
+
const { chain } = store;
|
|
27
|
+
const minify = o?.minify || {};
|
|
28
|
+
const { LightningCSSMinifyPlugin } = await import('./minimizer.js');
|
|
29
|
+
chain.optimization.minimizer('minCss').use(LightningCSSMinifyPlugin, [minify]);
|
|
30
|
+
};
|
|
31
|
+
//
|
|
32
|
+
const empLightningcssPlugin = (o) => {
|
|
33
|
+
return {
|
|
34
|
+
name: '@empjs/plugin-lightningcss',
|
|
35
|
+
async rsConfig(store) {
|
|
36
|
+
await Promise.all([uselightningcssLoader(store, o), uselightningcssMinify(store, o)]);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export default empLightningcssPlugin;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Visitor } from 'lightningcss';
|
|
2
|
+
import type { vwOptions } from './px-to-viewport.js';
|
|
3
|
+
type PxToRemType = {
|
|
4
|
+
rootValue?: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* // 插件编写 https://github.com/parcel-bundler/lightningcss/blob/master/node/test/visitor.test.mjs
|
|
8
|
+
*/
|
|
9
|
+
declare class PostcssConfig {
|
|
10
|
+
/**
|
|
11
|
+
* https://github.com/cuth/postcss-pxtorem
|
|
12
|
+
*/
|
|
13
|
+
px_to_rem(op?: PxToRemType): Visitor<any>;
|
|
14
|
+
/**
|
|
15
|
+
* https://github.com/evrone/postcss-px-to-viewport
|
|
16
|
+
*/
|
|
17
|
+
px_to_viewport(op: vwOptions): Visitor<any>;
|
|
18
|
+
/**
|
|
19
|
+
* https://www.npmjs.com/package/postcss-apply
|
|
20
|
+
*/
|
|
21
|
+
apply(defined: Map<any, any>): Visitor<any>;
|
|
22
|
+
/**
|
|
23
|
+
* https://www.npmjs.com/package/postcss-prefix-selector
|
|
24
|
+
*/
|
|
25
|
+
selector_prefix(): Visitor<any>;
|
|
26
|
+
/**
|
|
27
|
+
* https://www.npmjs.com/package/postcss-simple-vars
|
|
28
|
+
*/
|
|
29
|
+
static_vars(declared: Map<any, any>): Visitor<any>;
|
|
30
|
+
/**
|
|
31
|
+
* https://www.npmjs.com/package/postcss-url
|
|
32
|
+
*/
|
|
33
|
+
url(hostUrl: string): Visitor<any>;
|
|
34
|
+
/**
|
|
35
|
+
* https://www.npmjs.com/package/postcss-env-function
|
|
36
|
+
*/
|
|
37
|
+
specific_environment_variables(tokens: {
|
|
38
|
+
[k: string]: any;
|
|
39
|
+
}): Visitor<any>;
|
|
40
|
+
/**
|
|
41
|
+
* https://www.npmjs.com/package/postcss-env-function
|
|
42
|
+
*/
|
|
43
|
+
env_function(tokens: any): Visitor<any>;
|
|
44
|
+
/**
|
|
45
|
+
* https://www.npmjs.com/package/@csstools/postcss-design-tokens
|
|
46
|
+
*/
|
|
47
|
+
design_tokens(tokens: any): Visitor<any>;
|
|
48
|
+
/**
|
|
49
|
+
* https://github.com/csstools/custom-units
|
|
50
|
+
*/
|
|
51
|
+
custom_units(): Visitor<any>;
|
|
52
|
+
/**
|
|
53
|
+
* https://www.npmjs.com/package/postcss-property-lookup
|
|
54
|
+
*/
|
|
55
|
+
property_lookup(): Visitor<any>;
|
|
56
|
+
/**
|
|
57
|
+
* https://www.npmjs.com/package/postcss-focus-visible
|
|
58
|
+
*/
|
|
59
|
+
focus_visible(): Visitor<any>;
|
|
60
|
+
/**
|
|
61
|
+
* https://github.com/postcss/postcss-dark-theme-class
|
|
62
|
+
*/
|
|
63
|
+
dark_theme_class(): Visitor<any>;
|
|
64
|
+
/**
|
|
65
|
+
* https://github.com/postcss/postcss-100vh-fix
|
|
66
|
+
*/
|
|
67
|
+
fix_100vh(): Visitor<any>;
|
|
68
|
+
/**
|
|
69
|
+
* https://github.com/MohammadYounes/rtlcss
|
|
70
|
+
*/
|
|
71
|
+
logical_transforms(): Visitor<any>;
|
|
72
|
+
/**
|
|
73
|
+
* https://github.com/twbs/mq4-hover-shim
|
|
74
|
+
*/
|
|
75
|
+
hover_media_query(): Visitor<any>;
|
|
76
|
+
/**
|
|
77
|
+
* https://github.com/yunusga/postcss-momentum-scrolling
|
|
78
|
+
*/
|
|
79
|
+
momentum_scrolling(visitOverflow: any): Visitor<any>;
|
|
80
|
+
/**
|
|
81
|
+
* https://github.com/postcss/postcss-size
|
|
82
|
+
*/
|
|
83
|
+
size(): any;
|
|
84
|
+
}
|
|
85
|
+
declare const _default: PostcssConfig;
|
|
86
|
+
export default _default;
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { createPxToVwVisitor } from './px-to-viewport.js';
|
|
2
|
+
import { composeVisitors } from 'lightningcss';
|
|
3
|
+
/**
|
|
4
|
+
* // 插件编写 https://github.com/parcel-bundler/lightningcss/blob/master/node/test/visitor.test.mjs
|
|
5
|
+
*/
|
|
6
|
+
class PostcssConfig {
|
|
7
|
+
/**
|
|
8
|
+
* https://github.com/cuth/postcss-pxtorem
|
|
9
|
+
*/
|
|
10
|
+
px_to_rem(op = {}) {
|
|
11
|
+
const rv = op.rootValue ? op.rootValue : 16;
|
|
12
|
+
return {
|
|
13
|
+
Length(length) {
|
|
14
|
+
if (length.unit === 'px') {
|
|
15
|
+
return {
|
|
16
|
+
unit: 'rem',
|
|
17
|
+
value: length.value / rv,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* https://github.com/evrone/postcss-px-to-viewport
|
|
25
|
+
*/
|
|
26
|
+
px_to_viewport(op) {
|
|
27
|
+
op = op
|
|
28
|
+
? op
|
|
29
|
+
: {
|
|
30
|
+
designWidth: 320,
|
|
31
|
+
minPixelValue: 1,
|
|
32
|
+
};
|
|
33
|
+
return composeVisitors([createPxToVwVisitor(op)]);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* https://www.npmjs.com/package/postcss-apply
|
|
37
|
+
*/
|
|
38
|
+
apply(defined) {
|
|
39
|
+
defined = defined ? defined : new Map();
|
|
40
|
+
return {
|
|
41
|
+
Rule: {
|
|
42
|
+
style(rule) {
|
|
43
|
+
for (const selector of rule.value.selectors) {
|
|
44
|
+
if (selector.length === 1 && selector[0].type === 'type' && selector[0].name.startsWith('--')) {
|
|
45
|
+
defined.set(selector[0].name, rule.value.declarations);
|
|
46
|
+
return { type: 'ignored', value: null };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
rule.value.rules = rule.value.rules.filter(child => {
|
|
50
|
+
if (child.type === 'unknown' && child.value.name === 'apply') {
|
|
51
|
+
for (const token of child.value.prelude) {
|
|
52
|
+
if (token.type === 'dashed-ident' && defined.has(token.value)) {
|
|
53
|
+
const r = defined.get(token.value);
|
|
54
|
+
const decls = rule.value.declarations;
|
|
55
|
+
decls.declarations.push(...r.declarations);
|
|
56
|
+
decls.importantDeclarations.push(...r.importantDeclarations);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
return rule;
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* https://www.npmjs.com/package/postcss-prefix-selector
|
|
70
|
+
*/
|
|
71
|
+
selector_prefix() {
|
|
72
|
+
return {
|
|
73
|
+
Selector(selector) {
|
|
74
|
+
return [{ type: 'class', name: 'prefix' }, { type: 'combinator', value: 'descendant' }, ...selector];
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* https://www.npmjs.com/package/postcss-simple-vars
|
|
80
|
+
*/
|
|
81
|
+
static_vars(declared) {
|
|
82
|
+
declared = declared ? declared : new Map();
|
|
83
|
+
return {
|
|
84
|
+
Rule: {
|
|
85
|
+
unknown(rule) {
|
|
86
|
+
declared.set(rule.name, rule.prelude);
|
|
87
|
+
return [];
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
Token: {
|
|
91
|
+
'at-keyword'(token) {
|
|
92
|
+
if (declared.has(token.value)) {
|
|
93
|
+
return declared.get(token.value);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* https://www.npmjs.com/package/postcss-url
|
|
101
|
+
*/
|
|
102
|
+
url(hostUrl) {
|
|
103
|
+
return {
|
|
104
|
+
Url(url) {
|
|
105
|
+
url.url = hostUrl + url.url;
|
|
106
|
+
return url;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* https://www.npmjs.com/package/postcss-env-function
|
|
112
|
+
*/
|
|
113
|
+
specific_environment_variables(tokens) {
|
|
114
|
+
const ev = {};
|
|
115
|
+
for (const key in tokens) {
|
|
116
|
+
ev[key] = () => tokens[key];
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
EnvironmentVariable: ev,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* https://www.npmjs.com/package/postcss-env-function
|
|
124
|
+
*/
|
|
125
|
+
env_function(tokens) {
|
|
126
|
+
return {
|
|
127
|
+
EnvironmentVariable(env) {
|
|
128
|
+
if (env.name.type === 'custom') {
|
|
129
|
+
return tokens[env.name.ident];
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* https://www.npmjs.com/package/@csstools/postcss-design-tokens
|
|
136
|
+
*/
|
|
137
|
+
design_tokens(tokens) {
|
|
138
|
+
return {
|
|
139
|
+
Function: {
|
|
140
|
+
'design-token'(fn) {
|
|
141
|
+
if (fn.arguments.length === 1 &&
|
|
142
|
+
fn.arguments[0].type === 'token' &&
|
|
143
|
+
fn.arguments[0].value.type === 'string') {
|
|
144
|
+
return tokens[fn.arguments[0].value.value];
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* https://github.com/csstools/custom-units
|
|
152
|
+
*/
|
|
153
|
+
custom_units() {
|
|
154
|
+
return {
|
|
155
|
+
Token: {
|
|
156
|
+
dimension(token) {
|
|
157
|
+
if (token.unit.startsWith('--')) {
|
|
158
|
+
return {
|
|
159
|
+
type: 'function',
|
|
160
|
+
value: {
|
|
161
|
+
name: 'calc',
|
|
162
|
+
arguments: [
|
|
163
|
+
{
|
|
164
|
+
type: 'token',
|
|
165
|
+
value: {
|
|
166
|
+
type: 'number',
|
|
167
|
+
value: token.value,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
type: 'token',
|
|
172
|
+
value: {
|
|
173
|
+
type: 'delim',
|
|
174
|
+
value: '*',
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: 'var',
|
|
179
|
+
value: {
|
|
180
|
+
name: {
|
|
181
|
+
ident: token.unit,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* https://www.npmjs.com/package/postcss-property-lookup
|
|
195
|
+
*/
|
|
196
|
+
property_lookup() {
|
|
197
|
+
return {
|
|
198
|
+
Rule: {
|
|
199
|
+
style(rule) {
|
|
200
|
+
const valuesByProperty = new Map();
|
|
201
|
+
for (const decl of rule.value.declarations.declarations) {
|
|
202
|
+
let name = decl.property;
|
|
203
|
+
if (decl.property === 'unparsed') {
|
|
204
|
+
name = decl.value.propertyId.property;
|
|
205
|
+
}
|
|
206
|
+
valuesByProperty.set(name, decl);
|
|
207
|
+
}
|
|
208
|
+
rule.value.declarations.declarations = rule.value.declarations.declarations.map((decl) => {
|
|
209
|
+
// Only single value supported. Would need a way to convert parsed values to unparsed tokens otherwise.
|
|
210
|
+
if (decl.property === 'unparsed' && decl.value.value.length === 1) {
|
|
211
|
+
const token = decl.value.value[0];
|
|
212
|
+
if (token.type === 'token' &&
|
|
213
|
+
token.value.type === 'at-keyword' &&
|
|
214
|
+
valuesByProperty.has(token.value.value)) {
|
|
215
|
+
const v = valuesByProperty.get(token.value.value);
|
|
216
|
+
return {
|
|
217
|
+
/** @type any */
|
|
218
|
+
property: decl.value.propertyId.property,
|
|
219
|
+
value: v.value,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return decl;
|
|
224
|
+
});
|
|
225
|
+
return rule;
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* https://www.npmjs.com/package/postcss-focus-visible
|
|
232
|
+
*/
|
|
233
|
+
focus_visible() {
|
|
234
|
+
return {
|
|
235
|
+
Rule: {
|
|
236
|
+
style(rule) {
|
|
237
|
+
let clone = null;
|
|
238
|
+
for (const selector of rule.value.selectors) {
|
|
239
|
+
for (const [i, component] of selector.entries()) {
|
|
240
|
+
if (component.type === 'pseudo-class' && component.kind === 'focus-visible') {
|
|
241
|
+
if (clone == null) {
|
|
242
|
+
clone = [...rule.value.selectors.map(s => [...s])];
|
|
243
|
+
}
|
|
244
|
+
selector[i] = { type: 'class', name: 'focus-visible' };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (clone) {
|
|
249
|
+
return [rule, { type: 'style', value: { ...rule.value, selectors: clone } }];
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* https://github.com/postcss/postcss-dark-theme-class
|
|
257
|
+
*/
|
|
258
|
+
dark_theme_class() {
|
|
259
|
+
return {
|
|
260
|
+
Rule: {
|
|
261
|
+
media(rule) {
|
|
262
|
+
const q = rule.value.query.mediaQueries[0];
|
|
263
|
+
if (q.condition?.type === 'feature' &&
|
|
264
|
+
q.condition.value.type === 'plain' &&
|
|
265
|
+
q.condition.value.name === 'prefers-color-scheme' &&
|
|
266
|
+
q.condition.value.value.value === 'dark') {
|
|
267
|
+
const clonedRules = [rule];
|
|
268
|
+
for (const r of rule.value.rules) {
|
|
269
|
+
if (r.type === 'style') {
|
|
270
|
+
const clonedSelectors = [];
|
|
271
|
+
for (const selector of r.value.selectors) {
|
|
272
|
+
clonedSelectors.push([
|
|
273
|
+
{ type: 'type', name: 'html' },
|
|
274
|
+
{ type: 'attribute', name: 'theme', operation: { operator: 'equal', value: 'dark' } },
|
|
275
|
+
{ type: 'combinator', value: 'descendant' },
|
|
276
|
+
...selector,
|
|
277
|
+
]);
|
|
278
|
+
selector.unshift({ type: 'type', name: 'html' }, {
|
|
279
|
+
type: 'pseudo-class',
|
|
280
|
+
kind: 'not',
|
|
281
|
+
selectors: [[{ type: 'attribute', name: 'theme', operation: { operator: 'equal', value: 'light' } }]],
|
|
282
|
+
}, { type: 'combinator', value: 'descendant' });
|
|
283
|
+
}
|
|
284
|
+
clonedRules.push({ type: 'style', value: { ...r.value, selectors: clonedSelectors } });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return clonedRules;
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* https://github.com/postcss/postcss-100vh-fix
|
|
295
|
+
*/
|
|
296
|
+
fix_100vh() {
|
|
297
|
+
return {
|
|
298
|
+
Rule: {
|
|
299
|
+
style(style) {
|
|
300
|
+
let cloned;
|
|
301
|
+
for (const property of style.value.declarations.declarations) {
|
|
302
|
+
if (property.property === 'height' &&
|
|
303
|
+
property.value.type === 'length-percentage' &&
|
|
304
|
+
property.value.value.type === 'dimension' &&
|
|
305
|
+
property.value.value.value.unit === 'vh' &&
|
|
306
|
+
property.value.value.value.value === 100) {
|
|
307
|
+
if (!cloned) {
|
|
308
|
+
cloned = structuredClone(style);
|
|
309
|
+
cloned.value.declarations.declarations = [];
|
|
310
|
+
}
|
|
311
|
+
cloned.value.declarations.declarations.push({
|
|
312
|
+
...property,
|
|
313
|
+
value: {
|
|
314
|
+
type: 'stretch',
|
|
315
|
+
vendorPrefix: ['webkit'],
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (cloned) {
|
|
321
|
+
return [
|
|
322
|
+
style,
|
|
323
|
+
{
|
|
324
|
+
type: 'supports',
|
|
325
|
+
value: {
|
|
326
|
+
condition: {
|
|
327
|
+
type: 'declaration',
|
|
328
|
+
propertyId: {
|
|
329
|
+
property: '-webkit-touch-callout',
|
|
330
|
+
},
|
|
331
|
+
value: 'none',
|
|
332
|
+
},
|
|
333
|
+
loc: style.value.loc,
|
|
334
|
+
rules: [cloned],
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* https://github.com/MohammadYounes/rtlcss
|
|
345
|
+
*/
|
|
346
|
+
logical_transforms() {
|
|
347
|
+
return {
|
|
348
|
+
Rule: {
|
|
349
|
+
style(style) {
|
|
350
|
+
let cloned;
|
|
351
|
+
for (const property of style.value.declarations.declarations) {
|
|
352
|
+
if (property.property === 'transform') {
|
|
353
|
+
const clonedTransforms = property.value.map(transform => {
|
|
354
|
+
if (transform.type !== 'translateX') {
|
|
355
|
+
return transform;
|
|
356
|
+
}
|
|
357
|
+
if (!cloned) {
|
|
358
|
+
cloned = structuredClone(style);
|
|
359
|
+
cloned.value.declarations.declarations = [];
|
|
360
|
+
}
|
|
361
|
+
let value;
|
|
362
|
+
switch (transform.value.type) {
|
|
363
|
+
case 'dimension':
|
|
364
|
+
value = {
|
|
365
|
+
type: 'dimension',
|
|
366
|
+
value: { unit: transform.value.value.unit, value: -transform.value.value.value },
|
|
367
|
+
};
|
|
368
|
+
break;
|
|
369
|
+
case 'percentage':
|
|
370
|
+
value = { type: 'percentage', value: -transform.value.value };
|
|
371
|
+
break;
|
|
372
|
+
case 'calc':
|
|
373
|
+
value = { type: 'calc', value: { type: 'product', value: [-1, transform.value.value] } };
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
type: 'translateX',
|
|
378
|
+
value,
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
if (cloned) {
|
|
382
|
+
cloned.value.selectors.at(-1).push({ type: 'pseudo-class', kind: 'dir', direction: 'rtl' });
|
|
383
|
+
cloned.value.declarations.declarations.push({
|
|
384
|
+
...property,
|
|
385
|
+
value: clonedTransforms,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (cloned) {
|
|
391
|
+
return [style, cloned];
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* https://github.com/twbs/mq4-hover-shim
|
|
399
|
+
*/
|
|
400
|
+
hover_media_query() {
|
|
401
|
+
return {
|
|
402
|
+
Rule: {
|
|
403
|
+
media(media) {
|
|
404
|
+
const mediaQueries = media.value.query.mediaQueries;
|
|
405
|
+
if (mediaQueries.length === 1 &&
|
|
406
|
+
mediaQueries[0].condition &&
|
|
407
|
+
mediaQueries[0].condition.type === 'feature' &&
|
|
408
|
+
mediaQueries[0].condition.value.type === 'boolean' &&
|
|
409
|
+
mediaQueries[0].condition.value.name === 'hover') {
|
|
410
|
+
for (const rule of media.value.rules) {
|
|
411
|
+
if (rule.type === 'style') {
|
|
412
|
+
for (const selector of rule.value.selectors) {
|
|
413
|
+
selector.unshift({ type: 'class', name: 'hoverable' }, { type: 'combinator', value: 'descendant' });
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return media.value.rules;
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* https://github.com/yunusga/postcss-momentum-scrolling
|
|
425
|
+
*/
|
|
426
|
+
momentum_scrolling(visitOverflow) {
|
|
427
|
+
return {
|
|
428
|
+
Declaration: {
|
|
429
|
+
overflow: visitOverflow,
|
|
430
|
+
'overflow-x': visitOverflow,
|
|
431
|
+
'overflow-y': visitOverflow,
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* https://github.com/postcss/postcss-size
|
|
437
|
+
*/
|
|
438
|
+
size() {
|
|
439
|
+
return {
|
|
440
|
+
Declaration: {
|
|
441
|
+
custom: {
|
|
442
|
+
size(property) {
|
|
443
|
+
if (property.value[0].type === 'length') {
|
|
444
|
+
/** @type {import('../ast').Size} */
|
|
445
|
+
const value = { type: 'length-percentage', value: { type: 'dimension', value: property.value[0].value } };
|
|
446
|
+
return [
|
|
447
|
+
{ property: 'width', value },
|
|
448
|
+
{ property: 'height', value },
|
|
449
|
+
];
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
export default new PostcssConfig();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface Options {
|
|
2
|
+
designWidth: number;
|
|
3
|
+
minPixelValue: number;
|
|
4
|
+
excludeSelectors: {
|
|
5
|
+
type: string;
|
|
6
|
+
name: RegExp | string;
|
|
7
|
+
}[];
|
|
8
|
+
}
|
|
9
|
+
export type vwOptions = Partial<Options>;
|
|
10
|
+
export declare function createPxToVwVisitor(userOptions?: vwOptions): {
|
|
11
|
+
Selector(selectors: import("lightningcss").Selector): void;
|
|
12
|
+
Length(length: import("lightningcss").LengthValue): {
|
|
13
|
+
unit: "vw";
|
|
14
|
+
value: number;
|
|
15
|
+
} | undefined;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const baseOptions = {
|
|
2
|
+
designWidth: 320,
|
|
3
|
+
minPixelValue: 1,
|
|
4
|
+
excludeSelectors: [],
|
|
5
|
+
};
|
|
6
|
+
function createExcludeFilter(excludes) {
|
|
7
|
+
const isExclude = (testItem) => {
|
|
8
|
+
if (!testItem.name) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
for (const rule of excludes) {
|
|
12
|
+
if (testItem.type === rule.type) {
|
|
13
|
+
if (typeof rule.name === 'string' && rule.name === testItem.name) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (typeof rule.name === 'object' && rule.name.test(testItem.name)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
return isExclude;
|
|
24
|
+
}
|
|
25
|
+
export function createPxToVwVisitor(userOptions = {}) {
|
|
26
|
+
const options = Object.assign(baseOptions, userOptions);
|
|
27
|
+
const isExclude = createExcludeFilter(options.excludeSelectors);
|
|
28
|
+
let skipCurrentSelector = false;
|
|
29
|
+
return {
|
|
30
|
+
Selector(selectors) {
|
|
31
|
+
skipCurrentSelector = false;
|
|
32
|
+
for (const selector of selectors) {
|
|
33
|
+
if (isExclude(selector)) {
|
|
34
|
+
skipCurrentSelector = true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
Length(length) {
|
|
39
|
+
if (length.unit === 'px' && !skipCurrentSelector) {
|
|
40
|
+
if (length.value > options.minPixelValue) {
|
|
41
|
+
return {
|
|
42
|
+
unit: 'vw',
|
|
43
|
+
value: (length.value / options.designWidth) * 100,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@empjs/plugin-lightningcss",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "emp v3 lightningcss",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"maintainers": [
|
|
12
|
+
"xuhongbin",
|
|
13
|
+
"ckken"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/empjs/emp.git",
|
|
18
|
+
"directory": "packages/plugin-lightningcss"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "rimraf dist && pnpm run /^dev:.*/",
|
|
25
|
+
"dev:tsc": "tsc -w",
|
|
26
|
+
"dev:tsc-alias": "tsc-alias -w",
|
|
27
|
+
"dev:cjs": "cross-env ENV=dev node esbuild.js",
|
|
28
|
+
"build": "rimraf dist && pnpm run /^build:.*/",
|
|
29
|
+
"build:tsc": "tsc && tsc-alias",
|
|
30
|
+
"build:cjs": "cross-env ENV=prod node esbuild.js"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "ISC",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"browserslist": "^4.23.0",
|
|
40
|
+
"lightningcss": "^1.24.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@empjs/cli": "workspace:*",
|
|
44
|
+
"@rspack/core": "0.5.8",
|
|
45
|
+
"cross-env": "^7.0.3",
|
|
46
|
+
"esbuild": "^0.19.5",
|
|
47
|
+
"rimraf": "^5.0.5",
|
|
48
|
+
"tsc-alias": "^1.8.8"
|
|
49
|
+
}
|
|
50
|
+
}
|