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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
lib
test/dist*
node_modules
.DS_Store
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Webpack RTL Plugin [![Build Status](https://img.shields.io/travis/romainberger/webpack-rtl-plugin/master.svg?style=flat-square)](https://travis-ci.org/romainberger/webpack-rtl-plugin) [![npm version](https://img.shields.io/npm/v/webpack-rtl-plugin.svg?style=flat-square)](https://www.npmjs.com/package/webpack-rtl-plugin) [![npm downloads](https://img.shields.io/npm/dm/webpack-rtl-plugin.svg?style=flat-square)](https://www.npmjs.com/package/webpack-rtl-plugin)

Webpack plugin to use in addition to [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin) to create a second css bundle, processed to be rtl.
Webpack plugin to use in addition to [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to create a second css bundle, processed to be rtl.

This uses [rtlcss](https://github.com/MohammadYounes/rtlcss) under the hood, please refer to its documentation for supported properties.

Expand All @@ -17,7 +17,7 @@ $ npm install webpack-rtl-plugin
Add the plugin to your webpack configuration:

```js
import WebpackRTLPlugin from 'webpack-rtl-plugin'
const WebpackRTLPlugin = require('webpack-rtl-plugin')

module.exports = {
entry: path.join(__dirname, 'src/index.js'),
Expand All @@ -26,15 +26,25 @@ module.exports = {
filename: 'bundle.js',
},
module: {
loaders: [
rules: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader'),
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
...,
}
}
]
}
],
},
plugins: [
new ExtractTextPlugin('style.css'),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new WebpackRTLPlugin(),
],
}
Expand All @@ -55,8 +65,16 @@ new WebpackRTLPlugin({
})
```

* `filename` the filename of the result file. May contain `[contenthash]`. Default to `style.css`.
* `test` a RegExp (object or string) that must match asset filename
* `filename` the filename of the result file. May contain patterns in brackets. Default to `style.css`.
* `[contenthash]` a hash of the content of the extracted file
* `[id]` the module identifier
* `[name]` the module name
* `[file]` the extracted file filename
* `[filebase]` the extracted file basename
* `[ext]` the extracted file extension
* May be an array of replace function arguments like `[/(\.css)/i, '-rtl$1']`.
Replace applies to filename that specified in extract-text-webpack-plugin.
* `suffix` suffix added to the original base filename before the extension, if filename isn't provided. Default to `.rtl`.
* `options` Options given to `rtlcss`. See the [rtlcss documentation for available options](http://rtlcss.com/learn/usage-guide/options/).
* `plugins` RTLCSS plugins given to `rtlcss`. See the [rtlcss documentation for writing plugins](http://rtlcss.com/learn/extending-rtlcss/writing-a-plugin/). Default to `[]`.
Expand Down
34 changes: 12 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
{
"name": "webpack-rtl-plugin",
"version": "1.6.0",
"version": "2.0.0",
"description": "Webpack plugin to produce a rtl css bundle",
"main": "lib/index.js",
"main": "src/index.js",
"scripts": {
"clean": "rm -rf test/dist*",
"build": "rm -rf lib && babel src --out-dir lib",
"test": "npm run clean && npm run build && mocha --compilers js:babel-register test/*.js",
"prepublish": "npm run build"
"test": "rm -rf test/dist* && mocha test/*.js"
},
"author": "Romain Berger <romain@romainberger.com>",
"repository": {
Expand All @@ -25,24 +22,17 @@
],
"license": "MIT",
"devDependencies": {
"babel": "^6.5.2",
"babel-cli": "^6.6.5",
"babel-preset-es2015": "^6.6.0",
"babel-preset-stage-0": "^6.5.0",
"babel-register": "^6.7.2",
"chai": "^3.5.0",
"css-loader": "^0.23.1",
"extract-text-webpack-plugin": "^1.0.1",
"mocha": "^2.4.5",
"style-loader": "^0.13.1",
"webpack": "^1.13.0"
"chai": "4.2.0",
"css-loader": "2.1.1",
"mini-css-extract-plugin": "0.5.0",
"mocha": "6.0.2",
"webpack": "4.29.6"
},
"dependencies": {
"@romainberger/css-diff": "^1.0.3",
"async": "^2.0.0-rc.6",
"cssnano": "^3.7.1",
"postcss": "^5.0.21",
"rtlcss": "^2.0.4",
"webpack-sources": "^0.1.2"
"async": "^2.0.0",
"cssnano": "4.1.10",
"rtlcss": "2.4.0",
"webpack-sources": "1.3.0"
}
}
140 changes: 89 additions & 51 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,117 @@
import path from 'path'
import {createHash} from 'crypto'
import rtlcss from 'rtlcss'
import {ConcatSource} from 'webpack-sources'
import cssDiff from '@romainberger/css-diff'
import {forEachOfLimit} from 'async'
import cssnano from 'cssnano'

const WebpackRTLPlugin = function(options = {filename: false, suffix: false, options: {}, plugins: []}) {
this.options = options
}
const path = require('path')
const { createHash } = require('crypto')
const rtlcss = require('rtlcss')
const { ConcatSource } = require('webpack-sources')
const cssDiff = require('@romainberger/css-diff')
const { forEachOfLimit } = require('async')
const cssnano = require('cssnano')

const pluginName = "WebpackRTLPlugin"

class WebpackRTLPlugin {
constructor(options) {
this.options = {
filename: false,
options: {},
plugins: [],
suffix: false,
...options
}
}

WebpackRTLPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', (compilation, callback) => {
forEachOfLimit(compilation.chunks, 5, (chunk, key, cb) => {
var rtlFiles = [],
cssnanoPromise = Promise.resolve()
apply(compiler) {
compiler.hooks.emit.tapAsync(pluginName, (compilation, callback) => {
forEachOfLimit(compilation.chunks, 5, (chunk, key, cb) => {
const rtlFiles = []
let cssnanoPromise = Promise.resolve()

chunk.files.forEach(asset => {
const match = this.options.test ? new RegExp(this.options.test).test(asset) : true

if (path.extname(asset) !== '.css') {
return
}

chunk.files.forEach((asset) => {
if (path.extname(asset) === '.css') {
const baseSource = compilation.assets[asset].source()
let rtlSource = rtlcss.process(baseSource, this.options.options, this.options.plugins)
let filename
let rtlSource

if (this.options.filename) {
filename = this.options.filename
if (match) {
rtlSource = rtlcss.process(baseSource, this.options.options, this.options.plugins)

if (/\[contenthash\]/.test(this.options.filename)) {
const hash = createHash('md5').update(rtlSource).digest('hex').substr(0, 10)
filename = filename.replace('[contenthash]', hash)
if (this.options.filename instanceof Array && this.options.filename.length === 2) {
filename = asset.replace(this.options.filename[0], this.options.filename[1])
}
}
else {
const suffix = this.options.suffix || '.rtl'
const newFilename = `${path.basename(asset, '.css')}${suffix}`
else if (this.options.filename) {
filename = this.options.filename

filename = asset.replace(path.basename(asset, '.css'), newFilename)
}
if (/\[contenthash]/.test(this.options.filename)) {
const hash = createHash('md5').update(rtlSource).digest('hex').substr(0, 10)
filename = filename.replace('[contenthash]', hash)
}
if (/\[id]/.test(this.options.filename)) {
filename = filename.replace('[id]', chunk.id)
}
if (/\[name]/.test(this.options.filename)) {
filename = filename.replace('[name]', chunk.name)
}
if (/\[file]/.test(this.options.filename)) {
filename = filename.replace('[file]', asset)
}
if (/\[filebase]/.test(this.options.filename)) {
filename = filename.replace('[filebase]', path.basename(asset))
}
if (/\[ext]/.test(this.options.filename)) {
filename = filename.replace('.[ext]', path.extname(asset))
}
}
else {
const suffix = this.options.suffix || '.rtl'
const newFilename = `${path.basename(asset, '.css')}${suffix}`
filename = asset.replace(path.basename(asset, '.css'), newFilename)
}

if (this.options.diffOnly) {
rtlSource = cssDiff(baseSource, rtlSource)
if (this.options.diffOnly) {
rtlSource = cssDiff(baseSource, rtlSource)
}
}

if (this.options.minify !== false) {
let nanoOptions = {}
let nanoOptions = { from: undefined }
if (typeof this.options.minify === 'object') {
nanoOptions = this.options.minify
}

cssnanoPromise = cssnanoPromise.then(() => {

const rtlMinify = cssnano.process(rtlSource, nanoOptions).then(output => {
compilation.assets[filename] = new ConcatSource(output.css)
rtlFiles.push(filename)
});

const originalMinify = cssnano.process( baseSource, nanoOptions).then(output => {
let minify = cssnano.process( baseSource, nanoOptions).then(output => {
compilation.assets[asset] = new ConcatSource(output.css)
});

return Promise.all([rtlMinify,originalMinify]);
if (match) {
const rtlMinify = cssnano.process(rtlSource, nanoOptions).then(output => {
compilation.assets[filename] = new ConcatSource(output.css)
rtlFiles.push(filename)
});

minify = Promise.all([minify, rtlMinify]);
}

return minify;
})
}
else {
else if (match) {
compilation.assets[filename] = new ConcatSource(rtlSource)
rtlFiles.push(filename)
}
}
})

cssnanoPromise.then(() => {
chunk.files.push.apply(chunk.files, rtlFiles)
cb()
})
}, callback)
})
})

cssnanoPromise.then(() => {
chunk.files.push.apply(chunk.files, rtlFiles)
cb()
})
}, callback)
})
}
}

module.exports = WebpackRTLPlugin
Loading