Skip to content

Commit 87bae5e

Browse files
committed
Initial commit
0 parents  commit 87bae5e

File tree

11 files changed

+293
-0
lines changed

11 files changed

+293
-0
lines changed

.appveyor.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
environment:
2+
matrix:
3+
- nodejs_version: "6"
4+
- nodejs_version: "8"
5+
- nodejs_version: "10"
6+
7+
install:
8+
- npm install
9+
10+
test_script:
11+
- node --version
12+
- npm --version
13+
- npm test
14+
15+
build: off

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: node_js
2+
node_js:
3+
- "6"
4+
- "8"
5+
- "10"

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
[![Build Status](https://travis-ci.org/sasstools/json-importer.svg?branch=master)](https://travis-ci.org/sasstools/json-importer)
2+
3+
# json-importer
4+
5+
>Node Sass importer for for importing JSON files as maps
6+
7+
## Disclaimer
8+
9+
This is ALPHA software.
10+
11+
It's messy. It's probably slow. It's probably buggy.
12+
13+
Give it a shot. File bugs. Be patient.
14+
15+
## Support
16+
17+
- Node >= 6
18+
- node-sass >= 4.9.0
19+
20+
## Install
21+
22+
This package has a peer dependency on Node Sass for ensure import API compatibility.
23+
24+
```sh
25+
npm install @node-sass/json-importer node-sass
26+
```
27+
28+
## Usage
29+
30+
Create a JSON file you want to import.
31+
```json
32+
// config.json
33+
{
34+
"colors": {
35+
"red": "#f00",
36+
"blue": "#00f"
37+
},
38+
"sizes": ["16px", "20px", "24px"],
39+
}
40+
```
41+
42+
When Node Sass parses an `@import` for a `.json` URL it will try load the file from disk. If found the JSON object will be imported as a Sass map named after the `.json` file.
43+
44+
```scss
45+
@import "config.json";
46+
47+
$colors: map-get($config, "colors");
48+
$sizes: map-get($config, "sizes");
49+
50+
.button {
51+
color: map-get($color, "red");
52+
size: nth($sizes, 2);
53+
}
54+
```
55+
56+
Produces the following CSS output
57+
58+
```css
59+
.button {
60+
color: "#f00";
61+
size: "medium";
62+
}
63+
```
64+
65+
### Node Sass API
66+
67+
```js
68+
var sass = require('node-sass');
69+
var jsonImporter = require('@node-sass/json-importer');
70+
71+
sass.render({
72+
file: 'index.scss',
73+
importer: [jsonImporter],
74+
}, function (err, result) {
75+
if (err) throw err;
76+
console.log(result.css.toString());
77+
});
78+
```
79+
80+
### Node Sass CLI
81+
82+
```sh
83+
$ node-sass index.scss --importer node_modules/@node-sass/json-importer/index.js
84+
```
85+
86+
## Caveats
87+
88+
### Everything is a String
89+
90+
Sass has many types. `Number` which represent CSS numbers values with optional unit like `16px`. `Color` which represents CSS colour values like `red`, or `#f00`. These are structurally different from `String` like `"hello"`, `"16px"`, `"red"`, or `"#f00`.
91+
92+
To reduce complexity the values produced by this importer are always `String`. As a result you may need to `unquote()` the values to cast them into their intended types if for example you wanted to do math on them.

index.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const exists = (file) => {
5+
try {
6+
fs.accessSync(file, fs.constants.F_OK);
7+
return true;
8+
} catch (e) {
9+
return false;
10+
}
11+
}
12+
13+
const buildSassValue = (value) => {
14+
if (Array.isArray(value)) {
15+
return `(${value.reduce((prev, cur) => prev + `"${cur}",`, '')})`;
16+
}
17+
if (typeof value === "object") {
18+
return `(${buildSassMap(value)})`;
19+
}
20+
return `"${value}"`;
21+
}
22+
23+
const buildSassMap = (json) => {
24+
return Object.keys(json).reduce((prev, cur) => {
25+
return prev + `"${cur}": ${buildSassValue(json[cur])},`;
26+
}, '');
27+
}
28+
29+
const end = (done) => (value) => {
30+
return done ? done(value) : value;
31+
}
32+
33+
module.exports = function(url, prev, done) {
34+
done = end(done);
35+
if (!url) return done(null);
36+
37+
const parts = url.split('/');
38+
const name = path.basename(parts.pop(), '.json');
39+
const cwd = process.cwd();
40+
const resolved = path.join(cwd, url);
41+
42+
try {
43+
var json = require(resolved);
44+
} catch(err) {
45+
return done(err);
46+
}
47+
48+
return done({ contents: `$${name}: (${buildSassMap(json)});` });
49+
};

package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@node-sass/json-importer",
3+
"version": "1.2.3",
4+
"description": "Node Sass importer for importing JSON files as maps",
5+
"repository": "https://github.com/sasstools/json-importer",
6+
"keywords": [
7+
"node-sass",
8+
"sass",
9+
"importer",
10+
"json"
11+
],
12+
"main": "index.js",
13+
"scripts": {
14+
"test": "jest"
15+
},
16+
"engines": {
17+
"node": ">=6"
18+
},
19+
"author": "xzyfer",
20+
"license": "ISC",
21+
"peerDependencies": {
22+
"node-sass": "^4"
23+
},
24+
"devDependencies": {
25+
"jest": "^23.4.0",
26+
"node-sass": "^4.9.2"
27+
}
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`json-importer async should resolve flat json strings as Sass map 1`] = `
4+
"output {
5+
contents: (\\"color\\": \\"red\\", \\"size\\": \\"small\\"); }
6+
"
7+
`;
8+
9+
exports[`json-importer async should resolve json array values as Sass lists 1`] = `
10+
"output {
11+
contents: (\\"colors\\": \\"red green blue\\", \\"sizes\\": (\\"small\\", \\"medium\\", \\"big\\")); }
12+
"
13+
`;
14+
15+
exports[`json-importer async should resolve nested json strings as Sass map 1`] = `
16+
"output {
17+
contents: (\\"colors\\": (\\"red\\": \\"#f00\\", \\"blue\\": \\"#00f\\"), \\"sizes\\": (\\"small\\": \\"16px\\", \\"big\\": \\"30px\\")); }
18+
"
19+
`;
20+
21+
exports[`json-importer sync should resolve flat json strings as Sass map 1`] = `
22+
"output {
23+
contents: (\\"color\\": \\"red\\", \\"size\\": \\"small\\"); }
24+
"
25+
`;
26+
27+
exports[`json-importer sync should resolve json array values as Sass lists 1`] = `
28+
"output {
29+
contents: (\\"colors\\": \\"red green blue\\", \\"sizes\\": (\\"small\\", \\"medium\\", \\"big\\")); }
30+
"
31+
`;
32+
33+
exports[`json-importer sync should resolve nested json strings as Sass map 1`] = `
34+
"output {
35+
contents: (\\"colors\\": (\\"red\\": \\"#f00\\", \\"blue\\": \\"#00f\\"), \\"sizes\\": (\\"small\\": \\"16px\\", \\"big\\": \\"30px\\")); }
36+
"
37+
`;

tests/fixtures/flat.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"color": "red",
3+
"size": "small"
4+
}

tests/fixtures/list.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"colors": "red green blue",
3+
"sizes": ["small", "medium", "big"]
4+
}

tests/fixtures/nested.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"colors": {
3+
"red": "#f00",
4+
"blue": "#00f"
5+
},
6+
"sizes": {
7+
"small": "16px",
8+
"big": "30px"
9+
}
10+
}

tests/index.test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const path = require('path');
2+
const sass = require('node-sass');
3+
const importer = require('../');
4+
5+
const generate = (file) => `
6+
@import "${file}";
7+
output { contents: inspect($${path.basename(file, '.json')}) }
8+
`;
9+
10+
const compile = function(data) {
11+
return new Promise((yeah, nah) => {
12+
return sass.render(
13+
{ data, importer },
14+
(err, results) => err ? nah(err) : yeah(results.css.toString()),
15+
);
16+
});
17+
}
18+
19+
const compileSync = function(data) {
20+
return new Promise((yeah, nah) => {
21+
try {
22+
const results = sass.renderSync({ data, importer });
23+
yeah(results.css.toString());
24+
} catch (err) {
25+
nah(err);
26+
}
27+
});
28+
}
29+
30+
describe('json-importer', () => {
31+
[[ 'async', compile ], [ 'sync', compileSync ]].forEach(([ label, func ]) => {
32+
describe(label, () => {
33+
it('should resolve flat json strings as Sass map', () => (
34+
func(generate('tests/fixtures/flat.json'))
35+
.then(result => expect(result).toMatchSnapshot())
36+
));
37+
it('should resolve nested json strings as Sass map', () => (
38+
func(generate('tests/fixtures/nested.json'))
39+
.then(result => expect(result).toMatchSnapshot())
40+
));
41+
it('should resolve json array values as Sass lists', () => (
42+
func(generate('tests/fixtures/list.json'))
43+
.then(result => expect(result).toMatchSnapshot())
44+
));
45+
});
46+
});
47+
});

0 commit comments

Comments
 (0)