Skip to content

Commit

Permalink
Add include and exclude options to explicitly control compiled features
Browse files Browse the repository at this point in the history
This adds `include` and `exclude` options, which allow you to explicitly turn on or off certain features. These override the defaults based on the provided browser targets. For example, you might want to only compile colors, and handle auto prefixing or other features with another tool. Or you may want to handle everything except vendor prefixing with Lightning CSS. These options make that possible.

An enum of available feature flags is provided, and you can use the bitwise OR operator to combine flags together, e.g. `Features.Nesting | Features.Colors`. There are also some flags with turn on multiple flags at once, e.g. `Selectors`, `Colors`, and `MediaQueries`. The playground has been updated to show these flags and includes checkboxes to turn them on or off.

The Rust API has been updated to accept a new `Targets` struct instead of `Browsers`. This includes browser targets as a sub-field, along with the include and exclude options. Internally, the code has actually been simplified due to some new helper methods on the Targets struct which determine whether to compile certain features, and which vendor prefixes to include. This gets rid of a lot of option unwrapping throughout the code.
  • Loading branch information
devongovett committed May 29, 2023
1 parent c34af42 commit 09f6b7a
Show file tree
Hide file tree
Showing 65 changed files with 1,218 additions and 1,179 deletions.
8 changes: 4 additions & 4 deletions c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,9 @@ impl Into<MinifyOptions> for TransformOptions {

MinifyOptions {
targets: if self.targets != Targets::default() {
Some(self.targets.into())
Some(self.targets.into()).into()
} else {
None
Default::default()
},
unused_symbols,
}
Expand Down Expand Up @@ -333,9 +333,9 @@ pub extern "C" fn lightningcss_stylesheet_to_css(
},
source_map: source_map.as_mut(),
targets: if options.targets != Targets::default() {
Some(options.targets.into())
Some(options.targets.into()).into()
} else {
None
Default::default()
},
analyze_dependencies: if options.analyze_dependencies {
Some(Default::default())
Expand Down
5 changes: 3 additions & 2 deletions examples/custom_at_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ fn main() {

let result = stylesheet
.to_css(PrinterOptions {
targets: Some(Browsers {
targets: Browsers {
chrome: Some(100 << 16),
..Browsers::default()
}),
}
.into(),
..PrinterOptions::default()
})
.unwrap();
Expand Down
27 changes: 27 additions & 0 deletions node/flags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This file is autogenerated by build-prefixes.js. DO NOT EDIT!

exports.Features = {
Nesting: 1,
NotSelectorList: 2,
DirSelector: 4,
LangSelectorList: 8,
IsSelector: 16,
TextDecorationThicknessPercent: 32,
MediaIntervalSyntax: 64,
MediaRangeSyntax: 128,
CustomMediaQueries: 256,
ClampFunction: 512,
ColorFunction: 1024,
OklabColors: 2048,
LabColors: 4096,
P3Colors: 8192,
HexAlphaColors: 16384,
SpaceSeparatedColorNotation: 32768,
FontFamilySystemUi: 65536,
DoublePositionGradients: 131072,
VendorPrefixes: 262144,
LogicalProperties: 524288,
Selectors: 31,
MediaQueries: 448,
Colors: 64512,
};
10 changes: 7 additions & 3 deletions node/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock, ParsedComponent, Multiplier } from './ast';
import type { Targets } from './targets';
import { Targets, Features } from './targets';

export * from './ast';

export { Targets };
export { Targets, Features };

export interface TransformOptions<C extends CustomAtRules> {
/** The filename being transformed. Used for error messages and source maps. */
Expand All @@ -23,7 +23,11 @@ export interface TransformOptions<C extends CustomAtRules> {
projectRoot?: string,
/** The browser targets for the generated code. */
targets?: Targets,
/** Whether to enable various draft syntax. */
/** Features that should always be compiled, even when supported by targets. */
include?: Features,
/** Features that should never be compiled, even when unsupported by targets. */
exclude?: Features,
/** Whether to enable parsing various draft syntax. */
drafts?: Drafts,
/** Whether to enable various non-standard syntax. */
nonStandard?: NonStandard,
Expand Down
1 change: 1 addition & 0 deletions node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ if (process.env.CSS_TRANSFORMER_WASM) {

module.exports.browserslistToTargets = require('./browserslistToTargets');
module.exports.composeVisitors = require('./composeVisitors');
module.exports.Features = require('./flags').Features;
4 changes: 2 additions & 2 deletions node/index.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import index from './index.js';

const { transform, transformStyleAttribute, bundle, bundleAsync, browserslistToTargets, composeVisitors } = index;
export { transform, transformStyleAttribute, bundle, bundleAsync, browserslistToTargets, composeVisitors };
const { transform, transformStyleAttribute, bundle, bundleAsync, browserslistToTargets, composeVisitors, Features } = index;
export { transform, transformStyleAttribute, bundle, bundleAsync, browserslistToTargets, composeVisitors, Features };
44 changes: 37 additions & 7 deletions node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use lightningcss::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, Pr
use lightningcss::stylesheet::{
MinifyOptions, ParserFlags, ParserOptions, PrinterOptions, PseudoClasses, StyleAttribute, StyleSheet,
};
use lightningcss::targets::Browsers;
use lightningcss::targets::{Browsers, Features, Targets};
use lightningcss::visitor::Visit;
use parcel_sourcemap::SourceMap;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -492,6 +492,10 @@ struct Config {
#[serde(with = "serde_bytes")]
pub code: Vec<u8>,
pub targets: Option<Browsers>,
#[serde(default)]
pub include: u32,
#[serde(default)]
pub exclude: u32,
pub minify: Option<bool>,
pub source_map: Option<bool>,
pub input_source_map: Option<String>,
Expand Down Expand Up @@ -538,6 +542,10 @@ struct BundleConfig {
pub filename: String,
pub project_root: Option<String>,
pub targets: Option<Browsers>,
#[serde(default)]
pub include: u32,
#[serde(default)]
pub exclude: u32,
pub minify: Option<bool>,
pub source_map: Option<bool>,
pub drafts: Option<Drafts>,
Expand Down Expand Up @@ -654,16 +662,22 @@ fn compile<'i>(
stylesheet.visit(visitor).map_err(CompileError::JsError)?;
}

let targets = Targets {
browsers: config.targets,
include: Features::from_bits_truncate(config.include),
exclude: Features::from_bits_truncate(config.exclude),
};

stylesheet.minify(MinifyOptions {
targets: config.targets,
targets,
unused_symbols: config.unused_symbols.clone().unwrap_or_default(),
})?;

stylesheet.to_css(PrinterOptions {
minify: config.minify.unwrap_or_default(),
source_map: source_map.as_mut(),
project_root,
targets: config.targets,
targets,
analyze_dependencies: if let Some(d) = &config.analyze_dependencies {
match d {
AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }),
Expand Down Expand Up @@ -777,16 +791,22 @@ fn compile_bundle<
visit(&mut stylesheet).map_err(CompileError::JsError)?;
}

let targets = Targets {
browsers: config.targets,
include: Features::from_bits_truncate(config.include),
exclude: Features::from_bits_truncate(config.exclude),
};

stylesheet.minify(MinifyOptions {
targets: config.targets,
targets,
unused_symbols: config.unused_symbols.clone().unwrap_or_default(),
})?;

stylesheet.to_css(PrinterOptions {
minify: config.minify.unwrap_or_default(),
source_map: source_map.as_mut(),
project_root,
targets: config.targets,
targets,
analyze_dependencies: if let Some(d) = &config.analyze_dependencies {
match d {
AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }),
Expand Down Expand Up @@ -834,6 +854,10 @@ struct AttrConfig {
pub code: Vec<u8>,
pub targets: Option<Browsers>,
#[serde(default)]
pub include: u32,
#[serde(default)]
pub exclude: u32,
#[serde(default)]
pub minify: bool,
#[serde(default)]
pub analyze_dependencies: bool,
Expand Down Expand Up @@ -889,15 +913,21 @@ fn compile_attr<'i>(
attr.visit(visitor).unwrap();
}

let targets = Targets {
browsers: config.targets,
include: Features::from_bits_truncate(config.include),
exclude: Features::from_bits_truncate(config.exclude),
};

attr.minify(MinifyOptions {
targets: config.targets,
targets,
..MinifyOptions::default()
});
attr.to_css(PrinterOptions {
minify: config.minify,
source_map: None,
project_root: None,
targets: config.targets,
targets,
analyze_dependencies: if config.analyze_dependencies {
Some(DependencyOptions::default())
} else {
Expand Down
26 changes: 26 additions & 0 deletions node/targets.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,29 @@ export interface Targets {
safari?: number,
samsung?: number
}

export declare const enum Features {
Nesting = 1,
NotSelectorList = 2,
DirSelector = 4,
LangSelectorList = 8,
IsSelector = 16,
TextDecorationThicknessPercent = 32,
MediaIntervalSyntax = 64,
MediaRangeSyntax = 128,
CustomMediaQueries = 256,
ClampFunction = 512,
ColorFunction = 1024,
OklabColors = 2048,
LabColors = 4096,
P3Colors = 8192,
HexAlphaColors = 16384,
SpaceSeparatedColorNotation = 32768,
FontFamilySystemUi = 65536,
DoublePositionGradients = 131072,
VendorPrefixes = 262144,
LogicalProperties = 524288,
Selectors = 31,
MediaQueries = 448,
Colors = 64512,
}
44 changes: 43 additions & 1 deletion node/test/transform.test.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { transform } from '../index.mjs';
import { transform, Features } from 'lightningcss';
import { test } from 'uvu';
import * as assert from 'uvu/assert';

Expand All @@ -14,3 +14,45 @@ test('can enable non-standard syntax', () => {

assert.equal(res.code.toString(), '.foo>>>.bar{color:red}');
});

test('can enable features without targets', () => {
let res = transform({
filename: 'test.css',
code: Buffer.from('.foo { .bar { color: red }}'),
minify: true,
drafts: {
nesting: true
},
include: Features.Nesting
});

assert.equal(res.code.toString(), '.foo .bar{color:red}');
});

test('can disable features', () => {
let res = transform({
filename: 'test.css',
code: Buffer.from('.foo { color: lch(50.998% 135.363 338) }'),
minify: true,
targets: {
chrome: 80 << 16
},
exclude: Features.Colors
});

assert.equal(res.code.toString(), '.foo{color:lch(50.998% 135.363 338)}');
});

test('can disable prefixing', () => {
let res = transform({
filename: 'test.css',
code: Buffer.from('.foo { user-select: none }'),
minify: true,
targets: {
safari: 15 << 16
},
exclude: Features.VendorPrefixes
});

assert.equal(res.code.toString(), '.foo{user-select:none}');
});
Loading

0 comments on commit 09f6b7a

Please sign in to comment.