Skip to content

Commit

Permalink
camelCase (#90)
Browse files Browse the repository at this point in the history
additional options for `camelCase`.
  • Loading branch information
mightyaleksey committed Mar 20, 2017
1 parent b8df916 commit aaee8f7
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 69 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[GNUmakefile]
indent_style = tab

[Makefile]
indent_style = tab

[makefile]
indent_style = tab

# cheat sheet: http://EditorConfig.org
35 changes: 5 additions & 30 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,30 +1,5 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules/*

# added automatically by precommit-hook as defaults
.jshint*
.DS_Store
.npm
node_modules
npm-debug.log*
yarn.lock
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
language: node_js
node_js:
- "4.3"
- "4"
- "6"
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,12 @@ hook({
});
```

### `camelCase boolean`
### `camelCase boolean|string`

Camelizes exported class names. Similar to [css-loader?camelCase](https://github.com/webpack/css-loader#camel-case).

Available options: `true`, `dashes`, `only`, `dashesOnly`.

### `append` array

Appends custom plugins to the end of the PostCSS pipeline. Since the `require` function is synchronous, you should provide synchronous plugins only.
Expand Down
2 changes: 2 additions & 0 deletions lib/attachHook.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict';

/**
* @param {function} compile
* @param {string} extension
Expand Down
30 changes: 14 additions & 16 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
const assign = require('lodash').assign;
'use strict';

const {assign, identity, negate, camelCase: camelCaseFunc, mapKeys} = require('lodash');
const {dirname, relative, resolve} = require('path');
const {readFileSync} = require('fs');
const {transformTokens} = require('./transformTokens');

const attachHook = require('./attachHook');
const dirname = require('path').dirname;
const genericNames = require('generic-names');
const globToRegex = require('glob-to-regexp');
const identity = require('lodash').identity;
const negate = require('lodash').negate;
const camelCaseFunc = require('lodash').camelCase;
const mapKeys = require('lodash').mapKeys;
const readFileSync = require('fs').readFileSync;
const relative = require('path').relative;
const resolve = require('path').resolve;
const validate = require('./validate');

const postcss = require('postcss');
Expand All @@ -23,13 +21,13 @@ const debugFetch = require('debug')('css-modules:fetch');
const debugSetup = require('debug')('css-modules:setup');

module.exports = function setupHook({
camelCase,
devMode,
extensions = '.css',
ignore,
preprocessCss = identity,
processCss,
processorOpts,
camelCase,
append = [],
prepend = [],
createImportedName,
Expand Down Expand Up @@ -89,7 +87,6 @@ module.exports = function setupHook({
? require.resolve(_to)
: resolve(dirname(from), _to);


// checking cache
let tokens = tokensByFile[filename];
if (tokens) {
Expand All @@ -107,10 +104,6 @@ module.exports = function setupHook({

tokens = lazyResult.root.tokens;

if (camelCase) {
tokens = assign(mapKeys(tokens, (value, key) => camelCaseFunc(key)), tokens);
}

if (!debugMode) {
// updating cache
tokensByFile[filename] = tokens;
Expand All @@ -132,8 +125,13 @@ module.exports = function setupHook({
const exts = toArray(extensions);
const isException = buildExceptionChecker(ignore);

const hook = filename => {
const tokens = fetch(filename, filename);
return camelCase ? transformTokens(tokens, camelCase) : tokens;
};

// @todo add possibility to specify particular config for each extension
exts.forEach(extension => attachHook(filename => fetch(filename, filename), extension, isException));
exts.forEach(extension => attachHook(hook, extension, isException));
};

/**
Expand Down
63 changes: 63 additions & 0 deletions lib/transformTokens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use strict';

const {assign, camelCase, reduce} = require('lodash');

const camelizeKeys = (acc, value, key) => {
const camelizedKey = camelCase(key);
if (camelizedKey !== key) acc[camelizedKey] = value;
return acc;
};

const camelizeDashedKeys = (acc, value, key) => {
const camelizedKey = camelizeDashes(key);
if (camelizedKey !== key) acc[camelizedKey] = value;
return acc;
};

const camelizeOnlyKeys = (acc, value, key) => {
const camelizedKey = camelCase(key);
if (camelizedKey !== key) acc[camelizedKey] = value
else acc[key] = value;
return acc;
};

const camelizeOnlyDashedKeys = (acc, value, key) => {
const camelizedKey = camelizeDashes(key);
if (camelizedKey !== key) acc[camelizedKey] = value
else acc[key] = value;
return acc;
};

exports.camelizeDashes = camelizeDashes;
exports.transformTokens = transformTokens;

/**
* @param {string} str
* @return {string}
*/
function camelizeDashes(str) {
return str.replace(/-(\w)/g, (m, letter) => letter.toUpperCase());
}

/**
* @param {object} tokens
* @param {boolean|string} camelCase 'dashes|dashesOnly|only'
* @return {object}
*/
function transformTokens(tokens, camelCase) {
switch (camelCase) {
case true:
return reduce(tokens, camelizeKeys, assign({}, tokens));

case 'dashes':
return reduce(tokens, camelizeDashedKeys, assign({}, tokens));

case 'dashesOnly':
return reduce(tokens, camelizeOnlyDashedKeys, {});

case 'only':
return reduce(tokens, camelizeOnlyKeys, {});
}

return tokens;
}
30 changes: 20 additions & 10 deletions lib/validate.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
const difference = require('lodash').difference;
const forEach = require('lodash').forEach;
const keys = require('lodash').keys;
'use strict';

const {
difference,
forEach,
isArray,
isBoolean,
isFunction,
isPlainObject,
isRegExp,
isString,
keys,
} = require('lodash');

const rules = {
// hook
camelCase: 'boolean|string',
devMode: 'boolean',
extensions: 'array|string',
ignore: 'function|regex|string',
preprocessCss: 'function',
processCss: 'function',
processorOpts: 'object',
camelCase: 'boolean',
// plugins
append: 'array',
prepend: 'array',
Expand All @@ -23,12 +33,12 @@ const rules = {
};

const tests = {
array: require('lodash').isArray,
boolean: require('lodash').isBoolean,
function: require('lodash').isFunction,
object: require('lodash').isPlainObject,
regex: require('lodash').isRegExp,
string: require('lodash').isString,
array: isArray,
boolean: isBoolean,
function: isFunction,
object: isPlainObject,
regex: isRegExp,
string: isString,
};

module.exports = function validate(options) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"test": "npm run test:babel",
"test:babel": "NODE_PATH=$(pwd)/test/tokens/node_modules $npm_package_scripts_test_unit --compilers js:babel-register",
"test:coverage": "NODE_PATH=$(pwd)/test/tokens/node_modules babel-node --presets es2015 `npm bin`/isparta cover --report text --report html `npm bin`/_mocha -- --require test/setup.js --ui tdd test/*/*.js",
"test:node": "NODE_PATH=$(pwd)/test/tokens/node_modules $npm_package_scripts_test_unit --use_strict",
"test:watch": "NODE_PATH=$(pwd)/test/tokens/node_modules $npm_package_scripts_test_unit --watch --use_strict",
"test:node": "NODE_PATH=$(pwd)/test/tokens/node_modules $npm_package_scripts_test_unit",
"test:watch": "NODE_PATH=$(pwd)/test/tokens/node_modules $npm_package_scripts_test_unit --watch",
"test:unit": "mocha --require test/setup.js --ui tdd test/*/*.js"
},
"repository": {
Expand Down
2 changes: 2 additions & 0 deletions preset.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict';

const basename = require('path').basename;
const debug = require('debug')('css-modules:preset');
const dirname = require('path').dirname;
Expand Down
36 changes: 27 additions & 9 deletions test/api/camelCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,36 @@ const detachHook = require('../sugar').detachHook;
const dropCache = require('../sugar').dropCache;

suite('api/camelCase', () => {
test('should add camel case keys in token', () => {
const tokens = require('./fixture/bem.css');
assert.deepEqual(tokens, {
blockElementModifier: '_test_api_fixture_bem__block__element--modifier',
'block__element--modifier': '_test_api_fixture_bem__block__element--modifier',
suite('-> `true`', () => {
test('should add camel case keys in token', () => {
const tokens = require('./fixture/bem.css');
assert.deepEqual(tokens, {
blockElementModifier: '_test_api_fixture_bem__block__element--modifier',
'block__element--modifier': '_test_api_fixture_bem__block__element--modifier',
});
});

setup(() => hook({ camelCase: true }));

teardown(() => {
detachHook('.css');
dropCache('./api/fixture/bem.css');
});
});

setup(() => hook({ camelCase: true }));
suite('-> `dashesOnly`', () => {
test('should replace keys with dashes by its camel-cased equivalent', () => {
const tokens = require('./fixture/bem.css');
assert.deepEqual(tokens, {
'block__element-Modifier': '_test_api_fixture_bem__block__element--modifier',
});
});

setup(() => hook({camelCase: 'dashesOnly'}));

teardown(() => {
detachHook('.css');
dropCache('./api/fixture/bem.css');
teardown(() => {
detachHook('.css');
dropCache('./api/fixture/bem.css');
});
});
});
65 changes: 65 additions & 0 deletions test/lib/transformTokens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';

const {camelizeDashes, transformTokens} = require('../../lib/transformTokens');

suite('lib/transformTokens', () => {
test('camelizeDashes', () => {
assert.equal(camelizeDashes(''), '');
assert.equal(camelizeDashes('a-a'), 'aA');
assert.equal(camelizeDashes('a-a-b'), 'aAB');
assert.equal(camelizeDashes('a-'), 'a-');
});

suite('transformTokens', () => {
test('`true -> should transform all the keys to CC, keeps original keys', () => {
assert.deepEqual(transformTokens({
'k-e-bab': 'kebab case',
's_na_ke': 'snake case',
'simple': 'aaa',
}, true), {
'k-e-bab': 'kebab case',
's_na_ke': 'snake case',
'simple': 'aaa',
'kEBab': 'kebab case',
'sNaKe': 'snake case',
});
});

test('`dashes` -> should transform the keys with dashed, keeps original keys', () => {
assert.deepEqual(transformTokens({
'k-e-bab': 'kebab case',
's_na_ke': 'snake case',
'simple': 'aaa',
}, 'dashes'), {
'k-e-bab': 'kebab case',
's_na_ke': 'snake case',
'simple': 'aaa',
'kEBab': 'kebab case',
});
});

test('`dashesOnly` -> should transform only keys with dashes', () => {
assert.deepEqual(transformTokens({
'k-e-bab': 'kebab case',
's_na_ke': 'snake case',
'simple': 'aaa',
}, 'dashesOnly'), {
's_na_ke': 'snake case',
'simple': 'aaa',
'kEBab': 'kebab case',
});
});

test('`only` -> should camelize keys', () => {
assert.deepEqual(transformTokens({
'k-e-bab': 'kebab case',
's_na_ke': 'snake case',
'simple': 'aaa',
}, 'only'), {
'simple': 'aaa',
'kEBab': 'kebab case',
'sNaKe': 'snake case',
});
});
});
});

0 comments on commit aaee8f7

Please sign in to comment.