Skip to content
Open
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
4 changes: 2 additions & 2 deletions .eslintrc.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = {
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty-character-class": 2,
"no-empty-character-class": 0,
"no-empty": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast": 2,
Expand Down Expand Up @@ -77,7 +77,7 @@ module.exports = {
"no-extra-bind": 2,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implicit-coercion": 2,
"no-implicit-coercion": 0,
"no-implied-eval": 2,
"no-iterator": 2,
"no-labels": 2,
Expand Down
14 changes: 14 additions & 0 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ Example output:
"@foreground": "black"
}
```
Supports Less Maps:
```less
@colors: {
flat-blue : #4176A7;
dark-blue : darken(#4176A7, 20%);
}
```
Output:
```less
colors: {
'flat-blue' : '#4176A7',
'dark-blue' : 'darken(#4176A7, 20%)'
}
```
**Note:** while it does return variables it finds within rules, it is recommended to use this on files containing only variables, as it's not a parser and is designed to extract design principles for style guides.

### Options
Expand Down
2 changes: 1 addition & 1 deletion package.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@commitlint/config-conventional": "^7.0.1",
"babel-cli": "^6.7.7",
"babel-eslint": "^6.0.3",
"babel-istanbul": "^0.8.0",
"babel-istanbul": "^0.12.2",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.7.2",
Expand Down
123 changes: 113 additions & 10 deletions src/index.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,30 +1,119 @@
import stripComments from 'strip-json-comments';

const varRgx = /^[@$]/;
const followVar = (value, lessVars, dictionary) => {
if (varRgx.test(value)) {
// value is a variable
return followVar(lessVars[value] || dictionary[value.replace(varRgx, '')]);

const CASES = {
dash: {
testRgx: /-/,
split: str => str.toLowerCase().split('-'),
join: '-'
},
snake: {
testRgx: /_/,
split: str => str.toLowerCase().split('_'),
join: '_'
},
camel: {
testRgx: /[A-Z]/,
split: str => {
return str.replace(/(?!^[A-Z])([A-Z]+|[0-9]+)/g, ' $1').toLowerCase().split(' ');
},
join: ''
}
return value;
};

const replaceVariables = (value, lessVars, dictionary) => {
let replacedValue = value;
const matches = value.match(/(?:[@$][\w-.]*)/g) || [];

// Replace each matched variable within the value
matches.forEach(match => {
const mapped = match.split('.');
const replacement = mapped.length > 1 ?
lessVars[mapped[0]][mapped[1]] :
dictionary[match.replace(varRgx, '')] || lessVars[match];
replacedValue = replacedValue.replace(match, replacement);
});
return replacedValue;
};

const applyChangeCase = (key, changeCase) => {
let parts;
let joinStr = CASES.camel.join; // Default for sentence case to work
const prefix = varRgx.test(key) ? key.charAt(0) : '';

// Find what case the key is in
Object.keys(CASES).forEach(caseKey => {
// Strip the prefix and split into an array of word(s)
if (!parts && CASES[caseKey].testRgx.test(key)) {
parts = CASES[caseKey].split(key.replace(varRgx, ''));
}

// Use the CASES loop to find the join string
if (changeCase === caseKey) {
joinStr = CASES[caseKey].join;
}
});
// If parts is still empyt it was a single word
if (!parts) {
parts = [key.replace(varRgx, '')];
}
// Apply formatting based on the new case
parts = parts.map((part, i) => {
let rPart = part;
if (changeCase === 'camel' || changeCase === 'sentence') {
if (changeCase === 'sentence' || i > 0) {
rPart = rPart.charAt(0).toUpperCase() + rPart.slice(1);
}
}
return rPart;
});
// Put it all back together
return prefix + parts.join(joinStr);
};

export default (sheet, options = {}) => {
const { dictionary = {}, resolveVariables = false, stripPrefix = false } = options;
const { dictionary = {}, resolveVariables = false, stripPrefix = false, changeCase = false } = options;
let lessVars = {};
const matches = stripComments(sheet).match(/[@$](.*:[^;]*)/g) || [];
const matches = stripComments(sheet).match(/(?:[@$][\w-]*)\s*:\s*(?:\{.*?\}|[\s\w-#@()\/"':.%,]*)/gms) || [];

matches.forEach(variable => {
// Get an array with first element as the name of the less variable
const definition = variable.split(/:\s*/);
let value = definition.splice(1).join(':');
value = value.trim().replace(/^["'](.*)["']$/, '$1');

// Reduce the remaining elements to a single string and strip quotes
let value = definition.splice(1).join(':').trim().replace(/^["'](.*)["']$/, '$1');

// Check if the value was a Map
if (value.includes('{')) {
// Manipulate value into serialized JSON string
value = value.replace(/\n\s*/g, '').replace(/([\w-]*)\s*:([\s\w-#@()\/"'.%,]*);/gms, '"$1":"$2",').replace(/,}/, '}');

// Parse the string to JSON
try {
value = JSON.parse(value);
} catch (error) {
console.error('Malformed JSON Error, check the syntax in your less file', error);
}
}

// Add variable definition to the list
lessVars[definition[0].replace(/['"]+/g, '').trim()] = value;
});

if (resolveVariables) {
Object.keys(lessVars).forEach(key => {
const value = lessVars[key];
lessVars[key] = followVar(value, lessVars, dictionary);
// Check any nested values first
if (value.constructor && value.constructor === Object) {
Object.keys(value).forEach(key => {
const nestedValue = value[key];
value[key] = replaceVariables(nestedValue, lessVars, dictionary);
});
lessVars[key] = value;
} else {
lessVars[key] = replaceVariables(value, lessVars, dictionary);
}
});
}

Expand All @@ -37,5 +126,19 @@ export default (sheet, options = {}) => {
}, {});
}

if (changeCase) {
lessVars = Object.keys(lessVars).reduce((prev, key) => {
// Change case of map keys first
if (lessVars[key].constructor === Object) {
lessVars[key] = Object.keys(lessVars[key]).reduce((prev2, key2) => {
prev2[applyChangeCase(key2, changeCase)] = lessVars[key][key2];
return prev2;
}, {});
}
prev[applyChangeCase(key, changeCase)] = lessVars[key];
return prev;
}, {});
}

return lessVars;
};
Loading