Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
🐎 🐛 optimize plugin to initalize options only once
Also fixes problems with circular dependencies when used with babel-node and babel-register, #35

Also this provides functionality to use watch mode, only thing a developer has to do is to enable devMode, #33
  • Loading branch information
michalkvasnicak committed Mar 11, 2017
commit cc5d2c570f6f4d8e83c1f4aef5495a7d64f7c4b6
27 changes: 0 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,33 +175,6 @@ To extract all files in a single directory, give an object:
Note that `relativeRoot` is used to resolve relative directory names, available
as `[path]` in `filename` pattern.

## Using a `babel-register`

Make sure you set `ignore` option of `babel-register` to ignore all files used by css-modules-require-hook to process your css files.

**Require `babel-register` only once otherwise it will fail**
**Be aware, you need to explicitly ignore `node_modules` if you set `ignore` option**

```js
require('babel-register')({
ignore: /(processCss\.js|node_modules)/ // regex matching all files used by css-modules-require-hook to process your css files
})
```

## Using in mocha

Create a js file with content

**Be aware, you need to explicitly ignore `node_modules` if you set `ignore` option**

```js
require('babel-register')({
ignore: /(processCss\.js|node_modules)/ // regex matching all files used by css-modules-require-hook to process your css files
})
```

and then set this file as a compiler `--compilers js:<name-of-your-file>.js`

## Alternatives

- [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`.
Expand Down
113 changes: 69 additions & 44 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export default function transformCssModules({ types: t }) {

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

let matchExtensions = /\.css$/i;

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

return {
visitor: {
Program(path, state) {
if (initialized) {
return;
}

const currentConfig = { ...defaultOptions, ...state.opts };
// this is not a css-require-ook config
delete currentConfig.extractCss;

// match file extensions, speeds up transform by creating one
// RegExp ahead of execution time
matchExtensions = matcher(currentConfig.extensions);
const cssMap = new Map();
let thisPluginOptions = null;

// Add a space in current state for css filenames
state.$$css = {
styles: new Map()
};

const pushStylesCreator = (toWrap) => (css, filepath) => {
let processed;
const pluginApi = {
manipulateOptions(options) {
if (initialized || inProcessingFunction) {
return options;
}

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

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

if (!state.$$css.styles.has(filepath)) {
state.$$css.styles.set(filepath, processed);
extractCssFile(process.cwd(), filepath, processed, state);
}
// match file extensions, speeds up transform by creating one
// RegExp ahead of execution time
matchExtensions = matcher(currentConfig.extensions);

return processed;
};
const pushStylesCreator = (toWrap) => (css, filepath) => {
let processed;

// resolve options
Object.keys(requireHooksOptions).forEach(key => {
// skip undefined options
if (currentConfig[key] === undefined) {
return;
}
if (typeof toWrap === 'function') {
processed = toWrap(css, filepath);
}

currentConfig[key] = requireHooksOptions[key](currentConfig[key], currentConfig);
});
if (typeof processed !== 'string') processed = css;

// wrap or define processCss function that collect generated css
currentConfig.processCss = pushStylesCreator(currentConfig.processCss);
// set css content only if is new
if (!cssMap.has(filepath) || cssMap.get(filepath) !== processed) {
cssMap.set(filepath, processed);
}

require('css-modules-require-hook')(currentConfig);
return processed;
};

initialized = true;
},
// resolve options
Object.keys(requireHooksOptions).forEach(key => {
// skip undefined options
if (currentConfig[key] === undefined) {
return;
}

inProcessingFunction = true;
currentConfig[key] = requireHooksOptions[key](currentConfig[key], currentConfig);
inProcessingFunction = false;
});

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

require('css-modules-require-hook')(currentConfig);

initialized = true;

return options;
},
post() {
// extract css only if is this option set
if (thisPluginOptions.extractCss) {
// always rewrite file :-/
extractCssFile(
process.cwd(),
cssMap,
thisPluginOptions.extractCss
);
}
},
visitor: {
// import styles from './style.css';
ImportDefaultSpecifier(path, { file }) {
const { value } = path.parentPath.node.source;
Expand Down Expand Up @@ -162,4 +185,6 @@ export default function transformCssModules({ types: t }) {
}
}
};

return pluginApi;
}
53 changes: 21 additions & 32 deletions src/utils/extractCssFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,11 @@ export const PATH_VARIABLES = ['[path]', '[name]'];
* Extracts CSS to file
*
* @param {String} cwd
* @param {String} filepath
* @param {String} css
* @param {Object} state
* @param {Map} cssMap
* @param {String|Object} extractCss
* @returns {null}
*/
export default function extractCssFile(cwd, filepath, css, state) {
const { extractCss = null } = state.opts;

if (!extractCss) {
return null;
}

export default function extractCssFile(cwd, cssMap, extractCss) {
// this is the case where a single extractCss is requested
if (typeof(extractCss) === 'string') {
// check if extractCss contains some from pattern variables, if yes throw!
Expand All @@ -28,15 +21,9 @@ export default function extractCssFile(cwd, filepath, css, state) {
}
});

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

// this should output in a single file.
// Let's append the new file content.
return writeCssFile(extractCss, css, true);
return writeCssFile(extractCss, css);
}

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

// Make css file name relative to relativeRoot
const relativePath = relative(
resolve(cwd, relativeRoot),
filepath
);

const destination = join(
resolve(cwd, dir),
filename
)
.replace(/\[name]/, basename(filepath, extname(filepath)))
.replace(/\[path]/, dirname(relativePath));

writeCssFile(destination, css);
cssMap.forEach((css, filepath) => {
// Make css file name relative to relativeRoot
const relativePath = relative(
resolve(cwd, relativeRoot),
filepath
);

const destination = join(
resolve(cwd, dir),
filename
)
.replace(/\[name]/, basename(filepath, extname(filepath)))
.replace(/\[path]/, dirname(relativePath));

writeCssFile(destination, css);
});
}