-
Notifications
You must be signed in to change notification settings - Fork 47
/
index.js
129 lines (111 loc) · 3.92 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const cssPrefix = require('postcss-prefix')
const nodeResolve = require('resolve')
const mapLimit = require('map-limit')
const postcss = require('postcss')
const assert = require('assert')
const crypto = require('crypto')
const stackTrace = require('stack-trace')
const cssResolve = require('style-resolve').sync
const fs = require('fs')
const path = require('path')
module.exports = sheetify
module.exports.getPrefix = getPrefix
// transform css
// (str, str, obj?, fn) -> str
function sheetify (src, filename, options, done) {
// browserify transform
if (typeof src === 'string' && !/\n/.test(src) && filename && filename._flags) {
var args = Array.prototype.slice.apply(arguments)
return require('./transform.js').apply(this, args)
}
// handle tagged template calls directly from Node
const isTemplate = Array.isArray(src)
if (isTemplate) src = src.join('')
assert.equal(typeof src, 'string', 'src must be a string')
src = src.trim()
// Ensure prefix is always correct when run from inside node
var css
if (!isTemplate && (!filename || typeof filename === 'object')) {
// module or file name via tagged template call w or w/out options
const callerDirname = path.dirname(stackTrace.get()[1].getFileName())
const resolved = cssResolve(src, { basedir: callerDirname })
css = fs.readFileSync(resolved, 'utf8')
} else {
// it better be some css
css = src
}
const prefix = getPrefix(css)
// only parse if in a browserify transform
if (typeof filename === 'string') parseCss(src, filename, prefix, options, done)
return prefix
}
function getPrefix (css) {
const prefix = '_' + crypto.createHash('md5')
.update(css.trim())
.digest('hex')
.slice(0, 8)
return prefix
}
// parse css
// (str, str, str, obj, fn) -> null
function parseCss (src, filename, prefix, options, done) {
assert.equal(typeof filename, 'string', 'filename must be a string')
assert.equal(typeof prefix, 'string', 'prefix must be a string')
assert.equal(typeof options, 'object', 'options must be a object')
assert.equal(typeof done, 'function', 'done must be a function')
applyTransforms(filename, String(src), Object.assign({}, options), function (err, result) {
if (err) return done(err)
var p = postcss()
p = p.use(cssPrefix('.' + prefix))
try {
result.css = p.process(result.css).toString()
return done(null, result, prefix)
} catch (e) {
return done(e)
}
})
// apply transforms to a string of css,
// one at the time
// (str, str, obj, fn) -> null
function applyTransforms (filename, src, options, done) {
var current = { css: src, files: [] }
mapLimit(options.transform, 1, iterate, function (err) {
if (err) return done(err)
done(null, current)
})
// find and apply a transform to a string of css
// (fn, fn) -> null
function iterate (plugin, next) {
if (typeof plugin === 'string' || typeof plugin === 'function') {
plugin = [ plugin, {} ]
} else if (!Array.isArray(plugin)) {
return done(new Error('Plugin must be a string or array'))
}
const name = plugin[0]
const opts = plugin[1] || {}
if (typeof name === 'function') {
return transformLoaded(name, opts)
}
const resolveOpts = {
basedir: opts.basedir || options.basedir || process.cwd()
}
nodeResolve(name, resolveOpts, function (err, transformPath) {
if (err) return done(err)
const transform = require(transformPath)
transformLoaded(transform, opts)
})
function transformLoaded (transform, opts) {
transform(filename, current.css, opts, function (err, result) {
if (err) return next(err)
if (typeof result === 'string') {
current.css = result
} else {
current.css = result.css
current.files = current.files.concat(result.files)
}
next()
})
}
}
}
}