Skip to content

Commit a6499df

Browse files
committed
3.0.2
1 parent ef00b00 commit a6499df

File tree

8 files changed

+215
-150
lines changed

8 files changed

+215
-150
lines changed

.tape.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module.exports = {
4646
'import:array-array': {
4747
message: 'supports { importFrom: [["css", "test/import-root.css" ]] } usage',
4848
options: {
49-
importFrom: [['css', 'test/import-root.css' ]]
49+
importFrom: { from: 'test/import-root.css', type: 'css' }
5050
},
5151
expect: 'import.expect.css',
5252
result: 'import.result.css'
@@ -70,13 +70,19 @@ module.exports = {
7070
'import:object': {
7171
message: 'supports { importFrom: { customProperties: {} } } usage',
7272
options: {
73-
importFrom: {
74-
customProperties: {
75-
'--color-blue': 'blue',
76-
'--color-red': 'red',
77-
'--color': 'var(--color-blue)'
73+
importFrom: [
74+
{
75+
customProperties: {
76+
'--color': 'var(--color-blue)'
77+
}
78+
},
79+
{
80+
customProperties: {
81+
'--color-blue': 'blue',
82+
'--color-red': 'red'
83+
}
7884
}
79-
}
85+
]
8086
},
8187
expect: 'import.expect.css',
8288
result: 'import.result.css'

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changes to PostCSS color-mod() Function
22

3+
### 3.0.2 (September 23, 2018)
4+
5+
- Fixed an incompatibility with other `importFrom` plugins
6+
37
### 3.0.1 (September 18, 2018)
48

59
- Fixed an issue with using the `transparent` color keyword

index.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
1-
import { importCustomPropertiesFromSources, importCustomPropertiesFromCSSAST } from './lib/import';
1+
import getCustomProperties from './lib/get-custom-properties';
2+
import importCustomPropertiesFromSources from './lib/import-from';
23
import parser from 'postcss-values-parser';
34
import postcss from 'postcss';
45
import transformAST from './lib/transform';
56

67
export default postcss.plugin('postcss-color-mod-function', opts => {
8+
// how unresolved functions and arguments should be handled (default: "throw")
79
const unresolvedOpt = String(Object(opts).unresolved || 'throw').toLowerCase();
10+
11+
// how transformed colors will be produced in CSS
812
const stringifierOpt = Object(opts).stringifier || (color => color.toLegacy());
13+
14+
// sources to import custom selectors from
915
const importFrom = [].concat(Object(opts).importFrom || []);
16+
17+
// whether var() within color-mod() should use Custom Properties or var() fallback
1018
const transformVarsOpt = 'transformVars' in Object(opts) ? opts.transformVars : true;
1119

20+
// promise any custom selectors are imported
1221
const customPropertiesPromise = importCustomPropertiesFromSources(importFrom);
1322

1423
return async (root, result) => {
15-
const customProperties = Object.assign(await customPropertiesPromise, await importCustomPropertiesFromCSSAST(root));
24+
const customProperties = Object.assign(
25+
await customPropertiesPromise,
26+
getCustomProperties(root, { preserve: true })
27+
);
1628

1729
root.walkDecls(decl => {
1830
const originalValue = decl.value;

lib/get-custom-properties.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import valueParser from 'postcss-values-parser';
2+
3+
// return custom selectors from the css root, conditionally removing them
4+
export default function getCustomProperties(root, opts) {
5+
// initialize custom selectors
6+
const customPropertiesFromHtmlElement = {};
7+
const customPropertiesFromRootPsuedo = {};
8+
9+
// for each html or :root rule
10+
root.nodes.slice().forEach(rule => {
11+
const customPropertiesObject = isHtmlRule(rule)
12+
? customPropertiesFromHtmlElement
13+
: isRootRule(rule)
14+
? customPropertiesFromRootPsuedo
15+
: null;
16+
17+
// for each custom property
18+
if (customPropertiesObject) {
19+
rule.nodes.slice().forEach(decl => {
20+
if (isCustomDecl(decl)) {
21+
const { prop } = decl;
22+
23+
// write the parsed value to the custom property
24+
customPropertiesObject[prop] = valueParser(decl.value).parse();
25+
26+
// conditionally remove the custom property declaration
27+
if (!opts.preserve) {
28+
decl.remove();
29+
}
30+
}
31+
});
32+
33+
// conditionally remove the empty html or :root rule
34+
if (!opts.preserve && isEmptyParent(rule)) {
35+
rule.remove();
36+
}
37+
}
38+
});
39+
40+
// return all custom properties, preferring :root properties over html properties
41+
return { ...customPropertiesFromHtmlElement, ...customPropertiesFromRootPsuedo };
42+
}
43+
44+
// match html and :root rules
45+
const htmlSelectorRegExp = /^html$/i;
46+
const rootSelectorRegExp = /^:root$/i;
47+
const customPropertyRegExp = /^--[A-z][\w-]*$/;
48+
49+
// whether the node is an html or :root rule
50+
const isHtmlRule = node => node.type === 'rule' && htmlSelectorRegExp.test(node.selector) && Object(node.nodes).length;
51+
const isRootRule = node => node.type === 'rule' && rootSelectorRegExp.test(node.selector) && Object(node.nodes).length;
52+
53+
// whether the node is an custom property
54+
const isCustomDecl = node => node.type === 'decl' && customPropertyRegExp.test(node.prop);
55+
56+
// whether the node is a parent without children
57+
const isEmptyParent = node => Object(node.nodes).length === 0;

lib/import-from.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import postcss from 'postcss';
4+
import getCustomProperties from './get-custom-properties';
5+
6+
/* Import Custom Properties from CSS AST
7+
/* ========================================================================== */
8+
9+
function importCustomPropertiesFromCSSAST(root) {
10+
return getCustomProperties(root, { preserve: true });
11+
}
12+
13+
/* Import Custom Properties from CSS File
14+
/* ========================================================================== */
15+
16+
async function importCustomPropertiesFromCSSFile(from) {
17+
const css = await readFile(path.resolve(from));
18+
const root = postcss.parse(css, { from: path.resolve(from) });
19+
20+
return importCustomPropertiesFromCSSAST(root);
21+
}
22+
23+
/* Import Custom Properties from Object
24+
/* ========================================================================== */
25+
26+
function importCustomPropertiesFromObject(object) {
27+
const customProperties = Object.assign(
28+
{},
29+
Object(object).customProperties || Object(object)['custom-properties']
30+
);
31+
32+
return customProperties;
33+
}
34+
35+
/* Import Custom Properties from JSON file
36+
/* ========================================================================== */
37+
38+
async function importCustomPropertiesFromJSONFile(from) {
39+
const object = await readJSON(path.resolve(from));
40+
41+
return importCustomPropertiesFromObject(object);
42+
}
43+
44+
/* Import Custom Properties from JS file
45+
/* ========================================================================== */
46+
47+
async function importCustomPropertiesFromJSFile(from) {
48+
const object = await import(path.resolve(from));
49+
50+
return importCustomPropertiesFromObject(object);
51+
}
52+
53+
/* Import Custom Properties from Sources
54+
/* ========================================================================== */
55+
56+
export default function importCustomPropertiesFromSources(sources) {
57+
return sources.map(source => {
58+
if (source instanceof Promise) {
59+
return source;
60+
} else if (source instanceof Function) {
61+
return source();
62+
}
63+
64+
// read the source as an object
65+
const opts = source === Object(source) ? source : { from: String(source) };
66+
67+
// skip objects with Custom Properties
68+
if (opts.customProperties || opts['custom-properties']) {
69+
return opts
70+
}
71+
72+
// source pathname
73+
const from = String(opts.from || '');
74+
75+
// type of file being read from
76+
const type = (opts.type || path.extname(from).slice(1)).toLowerCase();
77+
78+
return { type, from };
79+
}).reduce(async (customProperties, source) => {
80+
const { type, from } = await source;
81+
82+
if (type === 'ast') {
83+
return Object.assign(await customProperties, importCustomPropertiesFromCSSAST(from));
84+
}
85+
86+
if (type === 'css') {
87+
return Object.assign(await customProperties, await importCustomPropertiesFromCSSFile(from));
88+
}
89+
90+
if (type === 'js') {
91+
return Object.assign(await customProperties, await importCustomPropertiesFromJSFile(from));
92+
}
93+
94+
if (type === 'json') {
95+
return Object.assign(await customProperties, await importCustomPropertiesFromJSONFile(from));
96+
}
97+
98+
return Object.assign(await customProperties, await importCustomPropertiesFromObject(await source));
99+
}, {});
100+
}
101+
102+
/* Helper utilities
103+
/* ========================================================================== */
104+
105+
const readFile = from => new Promise((resolve, reject) => {
106+
fs.readFile(from, 'utf8', (error, result) => {
107+
if (error) {
108+
reject(error);
109+
} else {
110+
resolve(result);
111+
}
112+
});
113+
});
114+
115+
const readJSON = async from => JSON.parse(await readFile(from));

lib/import.js

Lines changed: 0 additions & 131 deletions
This file was deleted.

0 commit comments

Comments
 (0)