Skip to content
This repository was archived by the owner on Dec 19, 2024. It is now read-only.

Add extract option to CLI #170

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/__tests__/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,22 @@ test("cli", function(t) {
)
planned += 1

exec(
cssnextBin + " --extract src/__tests__/fixtures/extract.css " +
"--config src/__tests__/fixtures/extract.json",
function(err, stdout) {
if (err) {
throw err
}
t.deepEqual(
JSON.parse(stdout),
JSON.parse(utils.readFixture("extract.expected", ".json")),
"should extract custom properties and custom medias on --extract"
)
}
)
planned += 1

exec(
cssnextBin + " --compress src/__tests__/fixtures/compress.css",
function(err, stdout) {
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/fixtures/extract.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:root {
--width: 1px;
--width: calc(1px + 1px);
--width2: var(--width);
}
@custom-media --mq (max-width: 30em);
12 changes: 12 additions & 0 deletions src/__tests__/fixtures/extract.expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"customProperties":{
"width": "2px",
"width2": "2px",
"color": "#e00",
"color2": "#e00"
},
"customMedias":{
"mq": "(max-width: 30em)",
"mq2": "screen"
}
}
15 changes: 15 additions & 0 deletions src/__tests__/fixtures/extract.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"features": {
"customProperties": {
"variables": {
"color": "#e00",
"color2": "var(--color)"
}
},
"customMedia": {
"extensions": {
"mq2": "screen"
}
}
}
}
2 changes: 2 additions & 0 deletions src/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ program
.option("-b, --browsers <items>", "browsers list (comma separated)")
.option("-I, --no-import", "do not inline @import")
.option("-U, --no-url", "do not adjust url()")
.option("-e, --extract", "output values of custom properties")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just custom props

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. Thanks for catching it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind I put "output defined variables" there? "custom properties and custom medias" are pretty wordy, and can get longer when new kinds of variables are speced.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"extract some computed values in a file (custom props, media, ...)" ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think width: calc(1px + 1px) => width: 2px also qualifies as a computed value. How about "extract resolved variables (custom props, media, etc)" ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "medias" is acceptable. This is from the oxford dictionary:

The word is also increasingly used in the plural form medias, as if it had a conventional singular form media, especially when referring to different forms of new media, and in the sense ‘the material or form used by an artist’: there were great efforts made by the medias of the involved countries

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, interesting :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't hate me, just a final nitpick: if you don't specify a output file path, the extracted json is sent to stdout, so strictly speaking, it's not always extracted into a file.

I won't complain if you want to keep it. :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will see how to handle that. I will probably add this to JS api too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. Thanks for your work.

.option("-c, --compress", "compress output")
.option("-s, --sourcemap", "add sourcemap")
.option("-w, --watch", "watch the input file for changes")
Expand Down Expand Up @@ -87,6 +88,7 @@ if ("compress" in program) {
if ("watch" in program) {
config.watch = program.watch
}
config.extract = program.extract

const input = program.args[0] ? path.resolve(program.args[0]) : null
const output = program.args[1] ? path.resolve(program.args[1]) : null
Expand Down
65 changes: 60 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,28 @@ function cssnext(string, options) {
)
)
) {
const plugin = cssnext.features[key](
typeof features[key] === "object"
? {...features[key]}
: undefined
)
let pluginOpts = typeof features[key] === "object"
? {...features[key]}
: undefined
if (options.extract) {
switch (key) {
case "customProperties":
if (!pluginOpts) {
pluginOpts = {}
}
pluginOpts.preserve = "computed"
pluginOpts.appendVariables = true
break
case "customMedia":
if (!pluginOpts) {
pluginOpts = {}
}
pluginOpts.preserve = true
pluginOpts.appendExtensions = true
break
}
}
const plugin = cssnext.features[key](pluginOpts)
plugin.postcssPlugin = "cssnext"
postcss.use(plugin)
}
Expand Down Expand Up @@ -159,6 +176,44 @@ function cssnext(string, options) {
if (typeof string === "string") {
const result = postcss.process(string, options)

// extract customProperties and customMedias
if (options.extract) {
const map = {
customProperties: {},
customMedias: {},
}
result.root.eachRule(function(rule) {
if (
rule.selectors.length !== 1 ||
rule.selectors[0] !== ":root" ||
rule.parent.type !== "root"
) {
return
}
rule.each(function(decl) {
let name = decl.prop
if (name.slice(0, 2) !== "--") {
return
}
name = name.slice(2)
map.customProperties[name] = decl.value
})
})
result.root.eachAtRule(function(atRule) {
if (atRule.name !== "custom-media") {
return
}
const params = atRule.params.split(" ")
let name = params.shift()
if (name.slice(0, 2) !== "--") {
return
}
name = name.slice(2)
map.customMedias[name] = params.join(" ")
})
return JSON.stringify(map) + "\n"
}

// default behavior, cssnext returns a css string if no or inline sourcemap
if (options.map === null || (options.map === true || options.map.inline)) {
return result.css
Expand Down