-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
249 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
a { | ||
font-size: 12px; | ||
color: blue; | ||
font-family: Roboto; | ||
} | ||
|
||
a { | ||
font-size: 13px; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
a { | ||
font-weight: bold; | ||
color: red; | ||
font-family: Roboto; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,6 @@ | ||
"use strict"; | ||
|
||
const fs = require("fs"); | ||
const util = require("util"); | ||
const postcss = require("postcss"); | ||
const glob = require("fast-glob"); | ||
const slashCSS = require("./src/slashCSS"); | ||
|
||
const getFileContent = util.promisify(fs.readFile); | ||
|
||
function slashCSSPlugin(opts = {}) { | ||
if (!opts || !opts.targets) { | ||
throw new Error( | ||
"This plugins needs an option object with a targets propertie" | ||
); | ||
} | ||
|
||
if (typeof opts.targets !== "string") { | ||
throw new Error("Targets option must be a string"); | ||
} | ||
|
||
return async root => { | ||
try { | ||
// get all external targets files | ||
const cssFilesPath = await glob(opts.targets); | ||
|
||
if (!cssFilesPath.length) { | ||
throw new Error("No css files found"); | ||
} | ||
|
||
const getFileContentPromises = cssFilesPath.map(filePath => | ||
getFileContent(filePath, "utf-8") | ||
); | ||
const cssFilesContent = await Promise.all(getFileContentPromises); | ||
|
||
cssFilesContent.forEach(targetCSSContent => { | ||
const targetsAST = postcss.parse(targetCSSContent).nodes; | ||
|
||
root.walkRules(rule => { | ||
// search for duplicate selector | ||
const findedAst = targetsAST.find( | ||
ast => ast.selector === rule.selector | ||
); | ||
|
||
if (findedAst) { | ||
rule.walkDecls(function(decl) { | ||
// if css properties are the sames (props and value) remove it | ||
if ( | ||
findedAst.nodes.some( | ||
prop => prop.prop === decl.prop && prop.value === decl.value | ||
) | ||
) { | ||
decl.remove(); | ||
} | ||
}); | ||
|
||
// if selector doesn't have any props then remove selector | ||
if (!rule.nodes || !rule.nodes.length) { | ||
rule.remove(); | ||
} | ||
} | ||
}); | ||
}); | ||
} catch (err) { | ||
throw err; | ||
} | ||
}; | ||
} | ||
|
||
module.exports = postcss.plugin("slashcss", slashCSSPlugin); | ||
module.exports = postcss.plugin("slashcss", slashCSS); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
"use strict"; | ||
|
||
const { MODES } = require("./constants"); | ||
|
||
module.exports = { | ||
formatAST, | ||
getSingleFormatedAST, | ||
} | ||
|
||
function formatAST(ast) { | ||
const formatedAST = {}; | ||
|
||
ast.forEach(item => { | ||
if (!formatedAST[item.selector]) { | ||
formatedAST[item.selector] = []; | ||
} | ||
|
||
item.nodes.forEach(({ prop, value }) => { | ||
formatedAST[item.selector].push(`${prop}|${value}`); | ||
}); | ||
}); | ||
|
||
return formatedAST; | ||
} | ||
|
||
function getSingleFormatedAST({asts, mode = MODES.ATLEAST_ONE} = {}) { | ||
if (!asts || !asts.length) { | ||
return {}; | ||
} | ||
|
||
if (asts.length === 1) { | ||
return formatedASTs[0]; | ||
} | ||
|
||
const singleAST = {}; | ||
|
||
const formatForAtleastOne = () => { | ||
asts.forEach(ast => { | ||
Object.keys(ast).forEach(selector => { | ||
if (!singleAST[selector]) { | ||
singleAST[selector] = []; | ||
} | ||
|
||
ast[selector].forEach(prop => { | ||
singleAST[selector].push(prop); | ||
}); | ||
}); | ||
}); | ||
}; | ||
|
||
const formatForAll = () => { | ||
const firstAST = asts.shift(); | ||
|
||
Object.keys(firstAST).forEach(selector => { | ||
const allHaveSelector = asts.every(singleAst => singleAst[selector]); | ||
|
||
if (allHaveSelector) { | ||
const properties = firstAST[selector]; | ||
|
||
properties.forEach(propertie => { | ||
if (asts.every(singleAst => singleAst[selector].includes(propertie))) { | ||
if (!singleAST[selector]) { | ||
singleAST[selector] = []; | ||
} | ||
|
||
singleAST[selector].push(propertie); | ||
} | ||
}); | ||
} | ||
}); | ||
}; | ||
|
||
if (mode === MODES.ATLEAST_ONE) { | ||
formatForAtleastOne(); | ||
} else { | ||
formatForAll(); | ||
} | ||
|
||
return singleAST; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"use strict"; | ||
|
||
module.exports = { | ||
MODES: { | ||
ATLEAST_ONE: "MatchAtleastOne", | ||
ALL: "MatchAll" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"use strict"; | ||
|
||
module.exports = { | ||
NO_CSS: "No css files found", | ||
OPTIONS_MISSING: "This plugins needs an option object with a targets propertie", | ||
OPTION_NOT_STRING: optionName => `${optionName} option must be a string`, | ||
INVALID_OPTION: optionName => `Invalid ${optionName} option value` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"use strict"; | ||
|
||
const fs = require("fs"); | ||
const util = require("util"); | ||
const postcss = require("postcss"); | ||
const glob = require("fast-glob"); | ||
|
||
const ERRORS = require("./errors"); | ||
const { MODES } = require("./constants"); | ||
const { formatAST, getSingleFormatedAST } = require("./ast"); | ||
|
||
const getFileContent = util.promisify(fs.readFile); | ||
|
||
module.exports = function slashCSSPlugin(opts = {}) { | ||
const options = Object.assign({}, opts); | ||
|
||
if (!options || !options.targets) { | ||
throw new Error(ERRORS.OPTIONS_MISSING); | ||
} | ||
|
||
if (typeof options.targets !== "string") { | ||
throw new Error(ERRORS.OPTION_NOT_STRING("Targets")); | ||
} | ||
|
||
if (options.mode) { | ||
if (typeof options.mode !== "string") { | ||
throw new Error(ERRORS.OPTION_NOT_STRING("Mode")); | ||
} | ||
|
||
if (Object.keys(MODES).every(key => MODES[key].toLowerCase() !== options.mode.toLowerCase())) { | ||
throw new Error(ERRORS.INVALID_OPTION("mode")); | ||
} | ||
} else { | ||
options.mode === MODES.ATLEAST_ONE; | ||
} | ||
|
||
return async (root) => { | ||
try { | ||
const cssFilesPath = await glob(options.targets); | ||
|
||
if (!cssFilesPath.length) { | ||
throw new Error(ERRORS.NO_CSS); | ||
} | ||
|
||
const getFileASTPromises = cssFilesPath.map(filePath => { | ||
return getFileContent(filePath, "utf-8") | ||
.then(cssContent => postcss.parse(cssContent).nodes) | ||
.then(postCSSAST => formatAST(postCSSAST)) | ||
}); | ||
|
||
const singleAST = getSingleFormatedAST({ | ||
asts: await Promise.all(getFileASTPromises), | ||
mode: options.mode | ||
}); | ||
|
||
root.walkRules(rule => { | ||
const targetSelector = singleAST[rule.selector]; | ||
|
||
if (targetSelector) { | ||
rule.walkDecls(function (decl) { | ||
if (targetSelector.includes(`${decl.prop}|${decl.value}`)) { | ||
decl.remove(); | ||
} | ||
}); | ||
|
||
if (!rule.nodes || !rule.nodes.length) { | ||
rule.remove(); | ||
} | ||
} | ||
}); | ||
} catch (err) { | ||
throw err; | ||
} | ||
} | ||
}; |