Skip to content

Commit 080b627

Browse files
committed
start porting js-to-less-var-loader
0 parents  commit 080b627

17 files changed

+540
-0
lines changed

.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["es2015"]
3+
}

.gitignore

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.DS_Store
2+
3+
# Logs
4+
logs
5+
*.log
6+
npm-debug.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
13+
# Temporary files
14+
*.swp
15+
*.swo
16+
17+
# Directory for instrumented libs generated by jscoverage/JSCover
18+
lib-cov
19+
20+
# Coverage directory used by tools like istanbul
21+
coverage
22+
coverage.data
23+
.coveralls.yml
24+
25+
# Dependency directories
26+
node_modules
27+
28+
# Optional npm cache directory
29+
.npm
30+
31+
# Optional REPL history
32+
.node_repl_history

.travis.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: node_js
2+
node_js:
3+
- "4.0"
4+
script:
5+
- npm run test:coverage

README.md

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
![Build Status](https://travis-ci.org/tompascall/js-to-sass-var-loader.svg?branch=master) [![Coverage Status](https://coveralls.io/repos/github/tompascall/js-to-sass-var-loader/badge.svg?branch=master)](https://coveralls.io/github/tompascall/js-to-sass-var-loader?branch=master)
2+
3+
## js-to-sass-var-loader
4+
5+
### A [Webpack]() loader to share data for sass variables between javascript modules and sass files
6+
7+
This loader is for that special case when you would like to import data from a javascript module into a sass file. The [sass loader](https://github.com/webpack-contrib/sass-loader) complains, because importing js module is not a valid sass instruction.
8+
9+
##### The loader only handles the case when you want to inject sass variables into a sass file via a javascript module.
10+
11+
#### Prerequisites
12+
13+
- Nodejs >= 4.0
14+
- [sass](http://sass-lang.com/) for css pre-processing
15+
- Webpack for module bundle
16+
17+
18+
#### Setting up Webpack config
19+
20+
Probably you use [sass-loader](https://github.com/webpack-contrib/sass-loader) with Webpack. The usage in this case is pretty simple: just put this loader before sass-loader in your webpack config:
21+
22+
```js
23+
{
24+
rules: [
25+
test: /\.sass$/,
26+
use: [
27+
{
28+
loader: "style-loader"
29+
},
30+
{
31+
loader: "css-loader"
32+
},
33+
{
34+
loader: "sass-loader"
35+
},
36+
{
37+
loader: "js-to-sass-var-loader"
38+
}
39+
]
40+
]
41+
}
42+
```
43+
44+
#### Usage
45+
46+
Let's assume we would like to store some variable data in a module like this:
47+
48+
```js
49+
// colors.js
50+
51+
const colors = {
52+
'fancy-white': '#FFFFFE',
53+
'fancy-black': '#000001'
54+
};
55+
56+
module.exports = colors;
57+
```
58+
59+
You can use this module in your favorite templates / frameworks etc., and you don't want to repeat yourself when using these colors in a sass file as variable (e.g. `$fancy-white: #FFFFFE; /*...*/ background-color: $fancy-white`). In this situation just require your module in the beginning of your sass module:
60+
```js
61+
require('relative/path/to/colors.js');
62+
63+
// ...
64+
.some-class {
65+
background-color: $fancy-white
66+
}
67+
// ...
68+
```
69+
70+
**The form of the required data is important**: it must be an object with key/values pair, the key will be the name of the sass variable.
71+
72+
#### Misc
73+
74+
You can use other require form (`require('relative/path/to/module').someProperty`), too.
75+
76+
#### Demo
77+
78+
You can try the loader via a small fake app in the `demo` folder:
79+
```sh
80+
cd demo
81+
npm i
82+
npm run demo
83+
```
84+
The webpack dev server serves the app on `localhost:8030`. In the app we share data between js and sass modules.
85+
86+
#### Development
87+
88+
Run tests with `npm test` or `npm run test:watch`.
89+
90+
The transformer is developed with tdd, so if you would like to contribute, please, write your tests for your new functionality, and send pull request to integrate your changes.

__tests__/index.spec.js

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import path from 'path';
2+
const loader = require('../index').default;
3+
const { operator } = require('../index');
4+
5+
describe('js-to-sass-vars-loader', () => {
6+
7+
describe('module', () => {
8+
const context = {
9+
context: path.resolve()
10+
};
11+
12+
it('exports a function', () => {
13+
expect(typeof loader).toEqual('function');
14+
});
15+
16+
it('calls operator.mergeVarsToContent with content and loader context', () => {
17+
spyOn(operator, 'mergeVarsToContent');
18+
loader.call(context, 'asdf');
19+
expect(operator.mergeVarsToContent).toHaveBeenCalledWith('asdf', context);
20+
21+
});
22+
23+
it('returns the result of mergeVarsToContent', () => {
24+
const content = 'require("./mocks/colors.js")\n' + '.someClass {\ncolor: $nice;\n}';
25+
const merged = operator.mergeVarsToContent(content, context);
26+
const result = loader.call(context, content);
27+
expect(result).toEqual(merged);
28+
});
29+
});
30+
31+
describe('divideContent', () => {
32+
it('divides the require (if it exists) from the content', () => {
33+
const content = "require('colors.js');\n" +
34+
".someClass { color: #fff;}";
35+
expect(operator.divideContent(content)[0]).toEqual("require('colors.js');");
36+
expect(operator.divideContent(content)[1]).toEqual("\n.someClass { color: #fff;}");
37+
});
38+
39+
it('gives back content if there is no require in content', () => {
40+
const content = ".someClass { color: #fff;}";
41+
expect(operator.divideContent(content)[0]).toEqual("");
42+
expect(operator.divideContent(content)[1]).toEqual(content);
43+
});
44+
45+
it('handles more requires when divide', () => {
46+
const content = "require('colors.js');\n" +
47+
"require('sizes.js');\n" +
48+
".someClass { color: #fff;}";
49+
expect(operator.divideContent(content)[0]).toEqual("require('colors.js');\n" + "require('sizes.js');");
50+
expect(operator.divideContent(content)[1]).toEqual("\n.someClass { color: #fff;}");
51+
});
52+
53+
it('handles the form of request("asdf").someProp', () => {
54+
const content = "require('corners.js').typeOne;\n" + ".someClass { color: #fff;}";
55+
expect(operator.divideContent(content)[0]).toEqual("require('corners.js').typeOne;");
56+
});
57+
});
58+
59+
describe('getModulePath', () => {
60+
it('extracts module paths and methodName into an array', () => {
61+
expect(operator.getModulePath('require("./mocks/colors.js");\n')).toEqual([{path: "./mocks/colors.js"}]);
62+
63+
expect(operator.getModulePath('require("./mocks/colors.js");\n' + 'require("./mocks/sizes.js");')).toEqual([{path: "./mocks/colors.js"}, {path:"./mocks/sizes.js"}]);
64+
65+
expect(operator.getModulePath('require("./mocks/corners.js").typeTwo;\n')).toEqual([{path: "./mocks/corners.js", methodName: 'typeTwo'}]);
66+
});
67+
});
68+
69+
describe('getVarData', () => {
70+
const context = { context: path.resolve()};
71+
72+
it('gets variable data by modulePath with context', () => {
73+
const varData = operator.getVarData([{path: './mocks/colors.js' }], context);
74+
expect(varData).toEqual({ white: '#fff', black: '#000'});
75+
});
76+
77+
it('merges module data if there are more requests', () => {
78+
const varData = operator.getVarData([{path:'./mocks/colors.js'}, {path:'./mocks/sizes.js'}], context);
79+
expect(varData).toEqual({ white: '#fff', black: '#000', small: '10px', large: '50px'});
80+
});
81+
82+
it('handles methodName if it is given', () => {
83+
const varData = operator.getVarData([{ path:'./mocks/corners.js', methodName: 'typeOne'}], context);
84+
expect(varData).toEqual({ tiny: '1%', medium: '3%'});
85+
});
86+
});
87+
88+
describe('transformToSassVars', () => {
89+
it('takes a hash object and transforms it to sass variables', () => {
90+
const colors = require('../mocks/colors.js');
91+
expect(operator.transformToSassVars(colors)).toEqual('$white: #fff;\n$black: #000;\n');
92+
});
93+
});
94+
95+
describe('mergeVarsToContent', () => {
96+
const context = {
97+
context: path.resolve()
98+
};
99+
100+
it('inserst vars to sass content', () => {
101+
const content = "require('./mocks/colors.js');\n" +
102+
".someClass { color: #fff;}";
103+
const [ moduleData, sassContent ] = operator.divideContent(content);
104+
const modulePath = operator.getModulePath(moduleData);
105+
const varData = operator.getVarData(modulePath, context);
106+
const sassVars = operator.transformToSassVars(varData);
107+
108+
expect(operator.mergeVarsToContent(content, context)).toEqual(sassVars + sassContent);
109+
});
110+
111+
it('gives back content as is if there is no requre', () => {
112+
const content = ".someClass { color: #fff;}";
113+
expect(operator.mergeVarsToContent(content, context)).toEqual(content);
114+
});
115+
});
116+
});

demo/colors.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = {
2+
'fancy-red': '#db0f3b',
3+
'fancy-blue': '#0f9adb',
4+
'fancy-yellow': '#e5c63b',
5+
'fancy-green': '#4fdd59',
6+
'fancy-white': '#fcfff7',
7+
'fancy-black': '#1f2120',
8+
'fancy-pink': '#d326c8',
9+
'fancy-lilac': '#941ece'
10+
};

demo/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<html>
2+
<head>
3+
<title>Loader Demo</title>
4+
<meta charset="UTF-8">
5+
</head>
6+
<body>
7+
<div id="selected-squares-container"></div>
8+
<span style="font-family: monospace; font-size: small">Choose color:</span>
9+
<select name="colors"></select>
10+
<button id="add-square-button">Add square</button>
11+
12+
<script src="bundle.js"></script>
13+
</body>
14+
</html>

demo/index.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import './style.scss';
2+
import colors from './colors';
3+
4+
const select = document.querySelector('select');
5+
const container = document.getElementById('selected-squares-container');
6+
7+
function createOptions () {
8+
const fragment = document.createDocumentFragment();
9+
for (let key in colors) {
10+
const option = document.createElement('option');
11+
option.textContent = key;
12+
option.setAttribute('value', colors[key]);
13+
fragment.appendChild(option);
14+
}
15+
select.appendChild(fragment);
16+
}
17+
18+
function createSquare (type) {
19+
const square = document.createElement('div');
20+
square.className = `square ${type}`;
21+
return square;
22+
}
23+
24+
function addSquare (e) {
25+
e.preventDefault();
26+
const type = select.options[select.selectedIndex].text;
27+
container.appendChild(createSquare(type));
28+
}
29+
30+
function setupButtonHandler () {
31+
document.getElementById('add-square-button')
32+
.addEventListener('click', addSquare);
33+
}
34+
35+
createOptions();
36+
setupButtonHandler();

demo/package.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "js-to-sass-var-loader-demo",
3+
"version": "1.0.1",
4+
"description": "Demo for js-to-sass-var-loader",
5+
"main": "index.js",
6+
"scripts": {
7+
"demo": "./node_modules/.bin/webpack-dev-server --config webpack.config.js"
8+
},
9+
"keywords": [
10+
"demo"
11+
],
12+
"author": "tompascall",
13+
"license": "ISC",
14+
"devDependencies": {
15+
"babel-core": "^6.24.0",
16+
"babel-loader": "^6.4.1",
17+
"babel-preset-env": "^1.3.2",
18+
"css-loader": "^0.28.0",
19+
"js-to-sass-var-loader": "^1.0.0",
20+
"node-sass": "^4.5.2",
21+
"sass-loader": "^6.0.3",
22+
"style-loader": "^0.16.1",
23+
"webpack": "^2.3.2",
24+
"webpack-dev-server": "^2.4.2"
25+
}
26+
}

demo/style.scss

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require('./colors.js');
2+
3+
.square {
4+
width: 100px;
5+
height: 100px;
6+
display: inline-block;
7+
}
8+
9+
.fancy-blue {
10+
background-color: $fancy-blue;
11+
}
12+
13+
.fancy-red {
14+
background-color: $fancy-red;
15+
}
16+
17+
.fancy-yellow {
18+
background-color: $fancy-yellow;
19+
}
20+
21+
.fancy-green {
22+
background-color: $fancy-green;
23+
}
24+
25+
.fancy-white {
26+
background-color: $fancy-white;
27+
}
28+
29+
.fancy-black {
30+
background-color: $fancy-black;
31+
}
32+
33+
.fancy-lilac {
34+
background-color: $fancy-lilac;
35+
}
36+
37+
.fancy-pink {
38+
background-color: $fancy-pink;
39+
}
40+

0 commit comments

Comments
 (0)