-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(parse): use AST for parsing declarations, removing usage of regex
extracted expression strings now includes specified flags such as '!global'
- Loading branch information
1 parent
bbf6694
commit dfc9796
Showing
11 changed files
with
112 additions
and
80 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 |
---|---|---|
@@ -1,17 +1,17 @@ | ||
{ | ||
"name": "sass-extract", | ||
"version": "0.5.3", | ||
"description": "", | ||
"description": "Extract structured variables from sass files. Fast and accurate.", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"compile": "babel -d lib/ src/", | ||
"prepublish": "npm run compile", | ||
"test": "npm run compile && mocha --compilers js:babel-core/register", | ||
"changelog": "conventional-changelog -i CHANGELOG.md -s -r 0" | ||
"test": "mocha --compilers js:babel-core/register", | ||
"changelog": "conventional-changelog -i CHANGELOG.md -s -r 0", | ||
"watch": "mocha --watch --reporter min --compilers js:babel-core/register" | ||
}, | ||
"repository": "jgranstrom/sass-extract", | ||
"author": "John Granström <[email protected]>", | ||
"description": "Extract structured variables from sass files. Fast and accurate.", | ||
"keywords": [ | ||
"sass", | ||
"scss", | ||
|
@@ -29,7 +29,9 @@ | |
"node-sass": "^3.8.0 || 4" | ||
}, | ||
"dependencies": { | ||
"bluebird": "^3.4.7" | ||
"bluebird": "^3.4.7", | ||
"query-ast": "^1.0.1", | ||
"scss-parser": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.22.2", | ||
|
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,51 +1,78 @@ | ||
import sass from 'node-sass'; | ||
import { parse, stringify } from 'scss-parser'; | ||
import createQueryWrapper from 'query-ast'; | ||
|
||
const REGEX_VARIABLE_GLOBAL_IMPLICIT = /(\$[\w-_]+)\s*:\s*((.*?[\r\n]?)+?);/g; | ||
const REGEX_VARIABLE_GLOBAL_EXPLICIT = /(\$[\w-_]+)\s*:\s*(.*?)\s+!global\s*;/g; | ||
const REGEX_DEEP_CONTEXT = /({[^{}]*})/g; | ||
const REGEX_COMMENTS = /(?:(?:\/\*[\w\W]*\*\/)|(?:\/\/[^\r\n]*[\r\n]?))/g; | ||
/** | ||
* Check if a declaration node has a '!' operator followed by a '<flag>' identifier in its value | ||
*/ | ||
function declarationHasFlag($ast, node, flag) { | ||
return $ast(node) | ||
.children('value').children('operator') | ||
.filter(operator => operator.node.value === '!') | ||
.nextAll('identifier') // nextAll to account for potential space tokens following operator | ||
.filter(identifier => identifier.node.value === flag) | ||
.length() > 0 | ||
} | ||
|
||
/** | ||
* Strip a string for all occurences that matches provided regex | ||
* Check if a declaration node has a '!' operator followed by the 'default' identifier in its value | ||
*/ | ||
function stripByRegex(data, regex) { | ||
let strippedData = data; | ||
function isExplicitGlobalDeclaration($ast, node) { | ||
return declarationHasFlag($ast, node, 'global'); | ||
} | ||
|
||
let match; | ||
while(match = strippedData.match(regex)) { | ||
strippedData = strippedData.replace(match[0], ''); | ||
} | ||
/** | ||
* Check if a declaration node has a '!' operator followed by the 'global' identifier in its value | ||
*/ | ||
function isDefaultDeclaration($ast, node) { | ||
return declarationHasFlag($ast, node, 'default'); | ||
} | ||
|
||
return strippedData; | ||
/** | ||
* Parse the raw expression of a variable declaration excluding flags | ||
*/ | ||
function parseExpression($ast, declaration) { | ||
let flagsReached = false; | ||
|
||
return stringify($ast(declaration) | ||
.children('value') | ||
.get(0)) | ||
.trim(); | ||
} | ||
|
||
/** | ||
* Extract variable declaration and expression from a chunk of sass source using provided regex | ||
* Parse declaration node into declaration object | ||
*/ | ||
function extractVariables(data, regex) { | ||
const variables = []; | ||
function parseDeclaration($ast, declaration) { | ||
const variable = {}; | ||
|
||
variable.declarationClean = $ast(declaration) | ||
.children('property') | ||
.value(); | ||
|
||
let matches; | ||
while(matches = regex.exec(data)) { | ||
const declaration = matches[1]; | ||
const expression = matches[2]; | ||
const declarationClean = declaration.replace('$', ''); | ||
variable.declaration = `$${variable.declarationClean}`; | ||
|
||
variables.push({ declaration, expression, declarationClean }); | ||
} | ||
variable.expression = parseExpression($ast, declaration); | ||
variable.flags = { | ||
default: isDefaultDeclaration($ast, declaration), | ||
global: isExplicitGlobalDeclaration($ast, declaration), | ||
}; | ||
|
||
return variables; | ||
return variable; | ||
} | ||
|
||
/** | ||
* Parse variables declarations from a chunk of sass source | ||
* Parse variable declarations from a chunk of sass source | ||
*/ | ||
export function parseDeclarations(data) { | ||
const decommentedData = stripByRegex(data, REGEX_COMMENTS); | ||
const decontextifiedData = stripByRegex(decommentedData, REGEX_DEEP_CONTEXT); | ||
const ast = parse(data); | ||
const $ast = createQueryWrapper(ast); | ||
|
||
const implicitGlobalDeclarations = $ast('declaration').hasParent('stylesheet'); | ||
const explicitGlobalDeclarations = $ast('declaration').hasParent('block') | ||
.filter(node => isExplicitGlobalDeclaration($ast, node)); | ||
|
||
const explicitGlobals = extractVariables(decommentedData, REGEX_VARIABLE_GLOBAL_EXPLICIT); | ||
const implicitGlobals = extractVariables(decontextifiedData, REGEX_VARIABLE_GLOBAL_IMPLICIT); | ||
let implicitGlobals = implicitGlobalDeclarations.map(declaration => parseDeclaration($ast, declaration)); | ||
let explicitGlobals = explicitGlobalDeclarations.map(declaration => parseDeclaration($ast, declaration)); | ||
|
||
return { explicitGlobals, implicitGlobals }; | ||
} |
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
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
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
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
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