Skip to content

Commit bea58d0

Browse files
Merge pull request #38 from michalkvasnicak/cache-and-circular-deps
Cache and circular deps
2 parents 02c7f94 + 5de9916 commit bea58d0

File tree

4 files changed

+91
-104
lines changed

4 files changed

+91
-104
lines changed

README.md

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -175,33 +175,6 @@ To extract all files in a single directory, give an object:
175175
Note that `relativeRoot` is used to resolve relative directory names, available
176176
as `[path]` in `filename` pattern.
177177

178-
## Using a `babel-register`
179-
180-
Make sure you set `ignore` option of `babel-register` to ignore all files used by css-modules-require-hook to process your css files.
181-
182-
**Require `babel-register` only once otherwise it will fail**
183-
**Be aware, you need to explicitly ignore `node_modules` if you set `ignore` option**
184-
185-
```js
186-
require('babel-register')({
187-
ignore: /(processCss\.js|node_modules)/ // regex matching all files used by css-modules-require-hook to process your css files
188-
})
189-
```
190-
191-
## Using in mocha
192-
193-
Create a js file with content
194-
195-
**Be aware, you need to explicitly ignore `node_modules` if you set `ignore` option**
196-
197-
```js
198-
require('babel-register')({
199-
ignore: /(processCss\.js|node_modules)/ // regex matching all files used by css-modules-require-hook to process your css files
200-
})
201-
```
202-
203-
and then set this file as a compiler `--compilers js:<name-of-your-file>.js`
204-
205178
## Alternatives
206179

207180
- [babel-plugin-transform-postcss](https://github.com/wbyoung/babel-plugin-transform-postcss) - which supports async plugins and does not depend on `css-modules-require-hook`.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "babel-plugin-css-modules-transform",
3-
"version": "1.2.1",
3+
"version": "1.2.3",
44
"description": "Transform required css modules so one can use generated class names.",
55
"main": "build/index.js",
66
"scripts": {

src/index.js

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export default function transformCssModules({ types: t }) {
4343

4444
// is css modules require hook initialized?
4545
let initialized = false;
46+
// are we requiring a module for preprocessCss, processCss, etc?
47+
// we don't want them to be transformed using this plugin
48+
// because it will cause circular dependency in babel-node and babel-register process
49+
let inProcessingFunction = false;
4650

4751
let matchExtensions = /\.css$/i;
4852

@@ -63,61 +67,80 @@ export default function transformCssModules({ types: t }) {
6367
);
6468
}
6569

66-
return {
67-
visitor: {
68-
Program(path, state) {
69-
if (initialized) {
70-
return;
71-
}
72-
73-
const currentConfig = { ...defaultOptions, ...state.opts };
74-
// this is not a css-require-ook config
75-
delete currentConfig.extractCss;
76-
77-
// match file extensions, speeds up transform by creating one
78-
// RegExp ahead of execution time
79-
matchExtensions = matcher(currentConfig.extensions);
70+
const cssMap = new Map();
71+
let thisPluginOptions = null;
8072

81-
// Add a space in current state for css filenames
82-
state.$$css = {
83-
styles: new Map()
84-
};
85-
86-
const pushStylesCreator = (toWrap) => (css, filepath) => {
87-
let processed;
73+
const pluginApi = {
74+
manipulateOptions(options) {
75+
if (initialized || inProcessingFunction) {
76+
return options;
77+
}
8878

89-
if (typeof toWrap === 'function') {
90-
processed = toWrap(css, filepath);
91-
}
79+
// find options for this plugin
80+
// we have to use this hack because plugin.key does not have to be 'css-modules-transform'
81+
// so we will identify it by comparing manipulateOptions
82+
thisPluginOptions = options.plugins.filter(
83+
([plugin]) => plugin.manipulateOptions === pluginApi.manipulateOptions
84+
)[0][1];
9285

93-
if (typeof processed !== 'string') processed = css;
86+
const currentConfig = { ...defaultOptions, ...thisPluginOptions };
87+
// this is not a css-require-ook config
88+
delete currentConfig.extractCss;
9489

95-
if (!state.$$css.styles.has(filepath)) {
96-
state.$$css.styles.set(filepath, processed);
97-
extractCssFile(process.cwd(), filepath, processed, state);
98-
}
90+
// match file extensions, speeds up transform by creating one
91+
// RegExp ahead of execution time
92+
matchExtensions = matcher(currentConfig.extensions);
9993

100-
return processed;
101-
};
94+
const pushStylesCreator = (toWrap) => (css, filepath) => {
95+
let processed;
10296

103-
// resolve options
104-
Object.keys(requireHooksOptions).forEach(key => {
105-
// skip undefined options
106-
if (currentConfig[key] === undefined) {
107-
return;
108-
}
97+
if (typeof toWrap === 'function') {
98+
processed = toWrap(css, filepath);
99+
}
109100

110-
currentConfig[key] = requireHooksOptions[key](currentConfig[key], currentConfig);
111-
});
101+
if (typeof processed !== 'string') processed = css;
112102

113-
// wrap or define processCss function that collect generated css
114-
currentConfig.processCss = pushStylesCreator(currentConfig.processCss);
103+
// set css content only if is new
104+
if (!cssMap.has(filepath) || cssMap.get(filepath) !== processed) {
105+
cssMap.set(filepath, processed);
106+
}
115107

116-
require('css-modules-require-hook')(currentConfig);
108+
return processed;
109+
};
117110

118-
initialized = true;
119-
},
111+
// resolve options
112+
Object.keys(requireHooksOptions).forEach(key => {
113+
// skip undefined options
114+
if (currentConfig[key] === undefined) {
115+
return;
116+
}
120117

118+
inProcessingFunction = true;
119+
currentConfig[key] = requireHooksOptions[key](currentConfig[key], currentConfig);
120+
inProcessingFunction = false;
121+
});
122+
123+
// wrap or define processCss function that collect generated css
124+
currentConfig.processCss = pushStylesCreator(currentConfig.processCss);
125+
126+
require('css-modules-require-hook')(currentConfig);
127+
128+
initialized = true;
129+
130+
return options;
131+
},
132+
post() {
133+
// extract css only if is this option set
134+
if (thisPluginOptions && thisPluginOptions.extractCss) {
135+
// always rewrite file :-/
136+
extractCssFile(
137+
process.cwd(),
138+
cssMap,
139+
thisPluginOptions.extractCss
140+
);
141+
}
142+
},
143+
visitor: {
121144
// import styles from './style.css';
122145
ImportDefaultSpecifier(path, { file }) {
123146
const { value } = path.parentPath.node.source;
@@ -162,4 +185,6 @@ export default function transformCssModules({ types: t }) {
162185
}
163186
}
164187
};
188+
189+
return pluginApi;
165190
}

src/utils/extractCssFile.js

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,11 @@ export const PATH_VARIABLES = ['[path]', '[name]'];
77
* Extracts CSS to file
88
*
99
* @param {String} cwd
10-
* @param {String} filepath
11-
* @param {String} css
12-
* @param {Object} state
10+
* @param {Map} cssMap
11+
* @param {String|Object} extractCss
1312
* @returns {null}
1413
*/
15-
export default function extractCssFile(cwd, filepath, css, state) {
16-
const { extractCss = null } = state.opts;
17-
18-
if (!extractCss) {
19-
return null;
20-
}
21-
14+
export default function extractCssFile(cwd, cssMap, extractCss) {
2215
// this is the case where a single extractCss is requested
2316
if (typeof(extractCss) === 'string') {
2417
// check if extractCss contains some from pattern variables, if yes throw!
@@ -28,15 +21,9 @@ export default function extractCssFile(cwd, filepath, css, state) {
2821
}
2922
});
3023

31-
// If this is the first file, then we should replace
32-
// old content
33-
if (state.$$css.styles.size === 1) {
34-
return writeCssFile(extractCss, css);
35-
}
24+
const css = Array.from(cssMap.values()).join('');
3625

37-
// this should output in a single file.
38-
// Let's append the new file content.
39-
return writeCssFile(extractCss, css, true);
26+
return writeCssFile(extractCss, css);
4027
}
4128

4229
// This is the case where each css file is written in
@@ -52,18 +39,20 @@ export default function extractCssFile(cwd, filepath, css, state) {
5239
throw new Error('[name] variable has to be used in extractCss.filename option');
5340
}
5441

55-
// Make css file name relative to relativeRoot
56-
const relativePath = relative(
57-
resolve(cwd, relativeRoot),
58-
filepath
59-
);
60-
61-
const destination = join(
62-
resolve(cwd, dir),
63-
filename
64-
)
65-
.replace(/\[name]/, basename(filepath, extname(filepath)))
66-
.replace(/\[path]/, dirname(relativePath));
67-
68-
writeCssFile(destination, css);
42+
cssMap.forEach((css, filepath) => {
43+
// Make css file name relative to relativeRoot
44+
const relativePath = relative(
45+
resolve(cwd, relativeRoot),
46+
filepath
47+
);
48+
49+
const destination = join(
50+
resolve(cwd, dir),
51+
filename
52+
)
53+
.replace(/\[name]/, basename(filepath, extname(filepath)))
54+
.replace(/\[path]/, dirname(relativePath));
55+
56+
writeCssFile(destination, css);
57+
});
6958
}

0 commit comments

Comments
 (0)