-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
transform: implement tagged templates
Supersedes GH-29 GH-38 Implements GH-31 Precedes GH-21 GH-34 GH-35 GH-39 This is a wip patch, as it touches almost every line of code currently present. It implements #31 mostly, but should provide the groundworks to further expand upon. Also implementing tests as we go to verify features work as advertised. The most notable change is that this patch now uses `falafel` to modify esprima nodes, as seen in [hyperxify](https://github.com/substack/hyperxify). This allows require calls to work both with template strings as with file requires. progress -------- - [x] detect and namespace template strings - [x] write tests for template string modification - [x] detect, read, and namespace files from disk - [x] write tests for reading and transforming files - [x] write tests for server - returns prefix if vanilla node - [ ] write tests for correct plugin resolution - [x] write tests for CSS injection in header in transform clean up test: apply std-fmt docs: annotate source {transform,index} -> {index,sheetify} docs: add tagged template docs [tmp] transform: add tests [tmp] transform: find template strings [tmp] transform: more stuff [tmp] transform: fix template strings [tmp] {sheetify, transform} -> {index,transform} [tmp] test/fixtures/ -> test/basic/ [tmp] deps: remove unused [tmp] test: test server [tmp] test: add coverage report [tmp] server: fix tagged templates [tmp] transform: detect disk calls [tmp] server: only parse if in transform [tmp] server: refactor core logic [tmp] index: fix assert calls [tmp] transform: make async [tmp] transform: fix and test [tmp] transform: fix disk read [tmp] deps: remove dead dev deps [tmp] test: rm dead fixtures [tmp] docs: add plugin docs [tmp] plugins: add tests [tmp] transform: fix from cli [tmp] docs: fix examples Signed-off-by: Yoshua Wuyts <i@yoshuawuyts.com> Closes #41
- Loading branch information
1 parent
e2f21df
commit da9c50d
Showing
21 changed files
with
394 additions
and
314 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,84 @@ | ||
const prefix = require('postcss-prefix') | ||
const resolve = require('style-resolve') | ||
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 fs = require('fs') | ||
const callerPath = require('caller-path') | ||
const path = require('path') | ||
|
||
module.exports = sheetify | ||
|
||
function sheetify (filename, options, done) { | ||
if (typeof options === 'function') { | ||
done = options | ||
options = {} | ||
} | ||
|
||
done = done || throwop | ||
options = options || {} | ||
|
||
// default basedir option to | ||
// path of module that called this module | ||
options.basedir = options.basedir || path.dirname(callerPath()) | ||
|
||
filename = resolve.sync(filename, { | ||
basedir: options.basedir | ||
}) | ||
// transform css | ||
// (str, str, obj?, fn) -> str | ||
function sheetify (src, filename, options, push) { | ||
// handle tagged template calls directly from Node | ||
if (Array.isArray(src)) src = src.join('') | ||
assert.equal(typeof src, 'string', 'src must be a string') | ||
|
||
var src = fs.readFileSync(filename) | ||
var id = '_' + crypto.createHash('md5') | ||
const prefix = '_' + crypto.createHash('md5') | ||
.update(src) | ||
.digest('hex') | ||
.slice(0, 8) | ||
|
||
src = postcss() | ||
.use(prefix('.' + id)) | ||
.process(src.toString()) | ||
.toString() | ||
// only parse if in a browserify transform | ||
if (filename) parseCss(src, filename, prefix, options, push) | ||
|
||
transform(filename, src, options, function (err, src) { | ||
return done(err, src, id) | ||
}) | ||
|
||
return id | ||
return prefix | ||
} | ||
|
||
function throwop (err) { | ||
if (err) throw err | ||
} | ||
// parse css | ||
// (str, str, str, obj, fn) -> null | ||
function parseCss (src, filename, prefix, options, next) { | ||
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 next, 'function', 'done must be a function') | ||
|
||
function transform (filename, src, options, done) { | ||
var use = options.use || [] | ||
use = Array.isArray(use) ? use.slice() : [use] | ||
const processedCss = postcss() | ||
.use(cssPrefix('.' + prefix)) | ||
.process(src.toString()) | ||
.toString() | ||
|
||
mapLimit(use, 1, iterate, function (err) { | ||
if (err) return done(err) | ||
done(null, src) | ||
next(function (done) { | ||
applyTransforms(filename, processedCss, options, function (err, css) { | ||
return done(err, css, prefix) | ||
}) | ||
}) | ||
|
||
function iterate (plugin, next) { | ||
if (typeof plugin === 'string') { | ||
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] || {} | ||
// apply transforms to a string of css, | ||
// one at the time | ||
// (str, str, obj, fn) -> null | ||
function applyTransforms (filename, src, options, done) { | ||
var use = options.use || [] | ||
use = Array.isArray(use) ? use.slice() : [ use ] | ||
|
||
nodeResolve(name, { | ||
basedir: opts.basedir || options.basedir | ||
}, function (err, transformPath) { | ||
mapLimit(use, 1, iterate, function (err) { | ||
if (err) return done(err) | ||
done(null, src) | ||
}) | ||
|
||
const transform = require(transformPath) | ||
|
||
transform(filename, src, opts, function (err, result) { | ||
if (err) return next(err) | ||
src = result | ||
next() | ||
// find and apply a transform to a string of css | ||
// (fn, fn) -> null | ||
function iterate (plugin, next) { | ||
if (typeof plugin === 'string') { | ||
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] || {} | ||
|
||
const resolveOpts = { basedir: opts.basedir || options.basedir } | ||
nodeResolve(name, resolveOpts, function (err, transformPath) { | ||
if (err) return done(err) | ||
|
||
const transform = require(transformPath) | ||
transform(filename, src, opts, function (err, result) { | ||
if (err) return next(err) | ||
src = result | ||
next() | ||
}) | ||
}) | ||
}) | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +0,0 @@ | ||
const test = require('tape') | ||
const path = require('path') | ||
const fs = require('fs') | ||
|
||
const sheetify = require('../') | ||
|
||
test('basic prefixing', function (t) { | ||
t.plan(1) | ||
|
||
const route = path.join(__dirname, 'fixtures', 'index-out.css') | ||
const expected = fs.readFileSync(route, 'utf8') | ||
|
||
const opts = { basedir: path.join(__dirname, 'fixtures') } | ||
sheetify('./index.css', opts, function (err, actual) { | ||
if (err) return t.error(err, 'no error') | ||
t.equal(actual, expected, 'output is as expected') | ||
}) | ||
}) | ||
|
||
test('basic prefixing', function (t) { | ||
t.plan(1) | ||
|
||
const route = path.join(__dirname, 'fixtures', 'index-out.css') | ||
const expected = fs.readFileSync(route, 'utf8') | ||
|
||
const opts = { | ||
basedir: path.join(__dirname, 'fixtures'), | ||
use: [[ 'sheetify-cssnext', { sourcemap: false } ]] | ||
} | ||
sheetify('./index.css', opts, function (err, actual) { | ||
if (err) return t.error(err, 'no error') | ||
t.equal(actual, expected, 'output is as expected') | ||
}) | ||
}) | ||
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
require('./basic') | ||
require('./disk') | ||
require('./plugins') | ||
require('./server') | ||
require('./transform') |
Oops, something went wrong.