Skip to content

Commit 60a63bf

Browse files
Merge pull request michalkvasnicak#20 from michalkvasnicak/feature/1.0
Feature/1.0
2 parents 14df2d0 + 88e2e61 commit 60a63bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+817
-163
lines changed

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,32 @@ npm install --save-dev babel-plugin-css-modules-transform
5353
**With custom options [css-modules-require-hook options](https://github.com/css-modules/css-modules-require-hook#tuning-options)**
5454

5555

56-
```json
56+
```js
5757
{
5858
"plugins": [
5959
[
6060
"css-modules-transform", {
61+
"append": [
62+
"npm-module-name",
63+
"./path/to/module-exporting-a-function.js"
64+
],
65+
"camelCase": false,
66+
"createImportedName": "npm-module-name",
67+
"createImportedName": "./path/to/module-exporting-a-function.js",
68+
"devMode": false,
69+
"extensions": [".css", ".scss", ".less"], // list extensions to process; defaults to .css
6170
"generateScopedName": "[name]__[local]___[hash:base64:5]", // in case you don't want to use a function
6271
"generateScopedName": "./path/to/module-exporting-a-function.js", // in case you want to use a function
6372
"generateScopedName": "npm-module-name",
73+
"ignore": "*css",
74+
"ignore": "./path/to/module-exporting-a-function-or-regexp.js",
6475
"preprocessCss": "./path/to/module-exporting-a-function.js",
6576
"preprocessCss": "npm-module-name",
6677
"processCss": "./path/to/module-exporting-a-function.js",
6778
"processCss": "npm-module-name",
68-
"extensions": [".css", ".scss", ".less"], // list extensions to process; defaults to .css
69-
"append": [
70-
"npm-module-name",
71-
"./path/to/module-exporting-a-function.js"
72-
],
79+
"processOpts": "npm-module-name",
80+
"processOpts": "./path/to/module/exporting-a-plain-object.js",
81+
"mode": "string",
7382
"prepend": [
7483
"npm-module-name",
7584
"./path/to/module-exporting-a-function.js"

src/index.js

Lines changed: 21 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import { resolve, dirname, basename, extname, isAbsolute, join, relative } from 'path';
2-
3-
import mkdirp from 'mkdirp';
4-
// *
52
import { writeFileSync, appendFileSync } from 'fs';
6-
/* /
7-
const writeFileSync = (file, content) => {
8-
console.log(`Will save ${file}\n${content.replace(/^/gm, ' ')}`);
9-
};
10-
// */
3+
import mkdirp from 'mkdirp';
4+
5+
// options resolvers
6+
import * as requireHooksOptions from './options_resolvers';
117

128
const writeCssFile = (filename, content) => {
139
mkdirp.sync(dirname(filename));
@@ -18,18 +14,6 @@ const appendCssFile = (filename, content) => {
1814
appendFileSync(filename, content);
1915
};
2016

21-
const simpleRequires = [
22-
'createImportedName',
23-
'generateScopedName',
24-
'processCss',
25-
'preprocessCss'
26-
];
27-
28-
const complexRequires = [
29-
'append',
30-
'prepend'
31-
];
32-
3317
const defaultOptions = {
3418
generateScopedName: '[name]__[local]___[hash:base64:5]'
3519
};
@@ -56,16 +40,23 @@ export default function transformCssModules({ types: t }) {
5640
const from = resolveModulePath(filepath);
5741
filePathOrModuleName = resolve(from, filePathOrModuleName);
5842
}
59-
return require(filePathOrModuleName);
43+
44+
// css-modules-require-hooks throws if file is ignored
45+
try {
46+
return require(filePathOrModuleName);
47+
} catch (e) {
48+
return {}; // return empty object, this simulates result of ignored stylesheet file
49+
}
6050
}
6151

6252
// is css modules require hook initialized?
6353
let initialized = false;
6454

6555
let matchExtensions = /\.css$/i;
56+
6657
function matcher(extensions = ['.css']) {
67-
const extensionsPatern = extensions.join('|').replace(/\./g, '\\\.');
68-
return new RegExp(`(${extensionsPatern})$`, 'i');
58+
const extensionsPattern = extensions.join('|').replace(/\./g, '\\\.');
59+
return new RegExp(`(${extensionsPattern})$`, 'i');
6960
}
7061

7162
return {
@@ -129,9 +120,11 @@ export default function transformCssModules({ types: t }) {
129120

130121
const pushStylesCreator = (toWrap) => (css, filepath) => {
131122
let processed;
123+
132124
if (typeof toWrap === 'function') {
133125
processed = toWrap(css, filepath);
134126
}
127+
135128
if (typeof processed !== 'string') processed = css;
136129

137130
if (!state.$$css.styles.has(filepath)) {
@@ -142,83 +135,19 @@ export default function transformCssModules({ types: t }) {
142135
return processed;
143136
};
144137

145-
// check if there are simple requires and if they are functions
146-
simpleRequires.forEach(key => {
147-
if (typeof currentConfig[key] !== 'string') {
148-
return;
149-
}
150-
151-
const modulePath = resolve(process.cwd(), currentConfig[key]);
152-
153-
// this one can be require or string
154-
if (key === 'generateScopedName') {
155-
try {
156-
// if it is existing file, require it, otherwise use value
157-
currentConfig[key] = require(modulePath);
158-
} catch (e) {
159-
try {
160-
currentConfig[key] = require(currentConfig[key]);
161-
} catch (_e) {
162-
// do nothing, because it is not a valid path
163-
}
164-
}
165-
166-
if (typeof currentConfig[key] !== 'function' && typeof currentConfig[key] !== 'string') {
167-
throw new Error(`Configuration '${key}' is not a string or function.`);
168-
}
169-
138+
// resolve options
139+
Object.keys(requireHooksOptions).forEach(key => {
140+
// skip undefined options
141+
if (currentConfig[key] === undefined) {
170142
return;
171143
}
172144

173-
if (currentConfig.hasOwnProperty(key)) {
174-
try {
175-
currentConfig[key] = require(modulePath);
176-
} catch (e) {
177-
try {
178-
currentConfig[key] = require(currentConfig[key]);
179-
} catch (_e) {
180-
// do nothing because it is not a valid path
181-
}
182-
}
183-
184-
if (typeof currentConfig[key] !== 'function') {
185-
throw new Error(`Module '${modulePath}' does not exist or is not a function.`);
186-
}
187-
}
145+
currentConfig[key] = requireHooksOptions[key](currentConfig[key], currentConfig);
188146
});
189147

190148
// wrap or define processCss function that collect generated css
191149
currentConfig.processCss = pushStylesCreator(currentConfig.processCss);
192150

193-
complexRequires.forEach(key => {
194-
if (!currentConfig.hasOwnProperty(key)) {
195-
return;
196-
}
197-
198-
if (!Array.isArray(currentConfig[key])) {
199-
throw new Error(`Configuration '${key}' has to be an array.`);
200-
}
201-
202-
currentConfig[key].forEach((plugin, index) => {
203-
// first try to load it using npm
204-
try {
205-
currentConfig[key][index] = require(plugin);
206-
} catch (e) {
207-
try {
208-
currentConfig[key][index] = require(resolve(process.cwd(), plugin));
209-
} catch (_e) {
210-
// do nothing
211-
}
212-
}
213-
214-
if (typeof currentConfig[key][index] !== 'function') {
215-
throw new Error(`Configuration '${key}' has to be valid path to a module at index ${index} or it does not export a function.`);
216-
}
217-
218-
currentConfig[key][index] = currentConfig[key][index]();
219-
});
220-
});
221-
222151
require('css-modules-require-hook')(currentConfig);
223152

224153
initialized = true;

src/options_resolvers/append.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { isFunction, isModulePath, requireLocalFileOrNodeModule } from '../utils';
2+
3+
/**
4+
* Resolves append option for css-modules-require-hook
5+
*
6+
* @param {*} value
7+
* @returns {Function}
8+
*/
9+
export default function append(value/* , currentConfig */) {
10+
if (Array.isArray(value)) {
11+
return value.map((option, index) => {
12+
if (isFunction(option)) {
13+
return option();
14+
} else if (isModulePath(option)) {
15+
const requiredOption = requireLocalFileOrNodeModule(option);
16+
17+
if (!isFunction(requiredOption)) {
18+
throw new Error(`Configuration 'append[${index}]' module is not exporting a function`);
19+
}
20+
21+
return requiredOption();
22+
}
23+
24+
throw new Error(`Configuration 'append[${index}]' is not a function or a valid module path`);
25+
});
26+
}
27+
28+
throw new Error(`Configuration 'append' is not an array`);
29+
}

src/options_resolvers/camelCase.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { isBoolean } from '../utils';
2+
3+
/**
4+
* Resolves camelCase option for css-modules-require-hook
5+
*
6+
* @param {*} value
7+
* @returns {boolean}
8+
*/
9+
export default function camelCase(value/* , currentConfig */) {
10+
if (!isBoolean(value)) {
11+
throw new Error(`Configuration 'camelCase' is not a boolean`);
12+
}
13+
14+
return value;
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { isFunction, isModulePath, requireLocalFileOrNodeModule } from '../utils';
2+
3+
/**
4+
* Resolves createImportedName css-modules-require-hook option
5+
*
6+
* @param {String|Function} value
7+
* @returns {Function}
8+
*/
9+
export default function createImportedName(value/* , currentConfig */) {
10+
if (isFunction(value)) {
11+
return value;
12+
} else if (isModulePath(value)) {
13+
const requiredOption = requireLocalFileOrNodeModule(value);
14+
15+
if (!isFunction(requiredOption)) {
16+
throw new Error(`Configuration file for 'createImportedName' is not exporting a function`);
17+
}
18+
19+
return requiredOption;
20+
}
21+
22+
throw new Error(`Configuration 'createImportedName' is not a function nor a valid module path`);
23+
}

src/options_resolvers/devMode.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { isBoolean } from '../utils';
2+
3+
/**
4+
* Resolves devMode option for css-modules-require-hook
5+
*
6+
* @param {*} value
7+
* @returns {boolean}
8+
*/
9+
export default function devMode(value/* , currentConfig */) {
10+
if (!isBoolean(value)) {
11+
throw new Error(`Configuration 'devMode' is not a boolean`);
12+
}
13+
14+
return value;
15+
}

src/options_resolvers/extensions.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { isString } from '../utils';
2+
3+
/**
4+
* Resolves extensions for css-modules-require-hook
5+
*
6+
* @param {*} value
7+
* @returns {Array.<String>}
8+
*/
9+
export default function extensions(value/* , currentConfig */) {
10+
if (Array.isArray(value)) {
11+
return value.map((extension, index) => {
12+
if (!isString(extension)) {
13+
throw new Error(`Configuration 'extensions[${index}]' is not a string`);
14+
}
15+
16+
return extension;
17+
});
18+
}
19+
20+
throw new Error(`Configuration 'extensions' is not an array`);
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { isModulePath, isFunction, isString, requireLocalFileOrNodeModule } from '../utils';
2+
3+
/**
4+
* Resolves generateScopedName option for css-modules-require-hook
5+
*
6+
* @param {String|Function} value
7+
*
8+
* @returns {String|Function}
9+
*/
10+
export default function generateScopedName(value/* ,currentConfig */) {
11+
if (isModulePath(value)) {
12+
const requiredModule = requireLocalFileOrNodeModule(value);
13+
14+
if (isString(requiredModule) || isFunction(requiredModule)) {
15+
return requiredModule;
16+
}
17+
18+
throw new Error(`Configuration file for 'generateScopedName' is not exporting a string nor a function`);
19+
} else if (isString(value) || isFunction(value)) {
20+
return value;
21+
} else {
22+
throw new Error(`Configuration 'generateScopedName' is not a function, string nor valid path to module`);
23+
}
24+
}

src/options_resolvers/ignore.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { isFunction, isModulePath, isRegExp, isString, requireLocalFileOrNodeModule } from '../utils';
2+
3+
/**
4+
* Resolves ignore option for css-modules-require-hook
5+
*
6+
* @param {*} value
7+
* @returns {Function|String|RegExp}
8+
*/
9+
export default function ignore(value/* , currentConfig */) {
10+
if (isFunction(value) || isRegExp(value)) {
11+
return value;
12+
} else if (isModulePath(value)) {
13+
const requiredOption = requireLocalFileOrNodeModule(value);
14+
15+
if (isFunction(requiredOption) || isString(requiredOption) || isRegExp(requiredOption)) {
16+
return requiredOption;
17+
}
18+
19+
throw new Error(`Configuration file for 'generateScopedName' is not exporting a string nor a function`);
20+
} else if (isString(value)) {
21+
return value;
22+
} else {
23+
throw new Error(`Configuration 'ignore' is not a function, string, RegExp nor valid path to module`);
24+
}
25+
}

src/options_resolvers/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export { default as append } from './append';
2+
export { default as camelCase } from './camelCase';
3+
export { default as createImportedName } from './createImportedName';
4+
export { default as devMode } from './devMode';
5+
export { default as generateScopedName } from './generateScopedName';
6+
export { default as ignore } from './ignore';
7+
export { default as mode } from './mode';
8+
export { default as prepend } from './prepend';
9+
export { default as preprocessCss } from './preprocessCss';
10+
export { default as processCss } from './processCss';
11+
export { default as processOpts } from './processOpts';
12+
export { default as rootDir } from './rootDir';
13+
export { default as use } from './use';

src/options_resolvers/mode.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { isString } from '../utils';
2+
3+
/**
4+
* Resolves mode option for css-modules-require-hook
5+
*
6+
* @param {*} value
7+
* @returns {boolean}
8+
*/
9+
export default function mode(value/* , currentConfig */) {
10+
if (!isString(value)) {
11+
throw new Error(`Configuration 'mode' is not a string`);
12+
}
13+
14+
return value;
15+
}

0 commit comments

Comments
 (0)