-
Notifications
You must be signed in to change notification settings - Fork 184
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
222 additions
and
92 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
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,25 @@ | ||
# react-server-module-tagger | ||
|
||
A function for tagging [react-server](https://www.npmjs.com/package/react-server) | ||
logger instances with information about the module they're being used in. | ||
|
||
To transpile your source for use with | ||
[React Server](https://www.npmjs.com/package/react-server), install gulp and the plugin | ||
|
||
```shell | ||
npm i -D gulp react-server-module-tagger | ||
``` | ||
|
||
Then require and call the function. The tagger expects to have config and file | ||
data on its prototype, so use `.bind`. | ||
|
||
```javascript | ||
const tagger = require('react-server-module-tagger'); | ||
const filepath = 'path/to/my/output.js'; | ||
const optString = '({label:"foo"})' | ||
const moduleTag = tagger.bind({ file: { path: filepath }, config: { trim: 'path/to'} })(filepath, optString)); | ||
``` | ||
|
||
returns a logger instance that will have consistent coloring on the server and | ||
the client, and that has a human-friendly, readable name that easily maps to | ||
the file tree (in this example `components.my-feature.foo`). |
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,129 @@ | ||
var isWindows = ('win32' === process.platform); | ||
|
||
/** | ||
* A util function for tagging modules. Expects to find data on its prototype | ||
* as follows | ||
* { | ||
* file: { | ||
* path: the path to the file to tag | ||
* } | ||
* config: { | ||
* trim: the string to trim off the front of the logger name | ||
* } | ||
* } | ||
* @example | ||
* const file = '/path/to/my/file.js'; | ||
* loggerSpec.bind({file, { trim: 'path.to.' }})(file, '({label: "foo"})') | ||
* // returns '{\"label\":\"foo\",\"name\":\"my.file\",\"color\":{\"server\":87,\"client\":\"rgb(42,212,212)\"}}' | ||
* @param {String} fullMatch May also be provided as this.file.path, the path to the file | ||
* @param {String} optString The label to add to the module tag, in the form '({label:"$label"})' | ||
* @return {String} A json object containing a module identifier | ||
*/ | ||
module.exports = function(fullMatch, optString){ | ||
var fn = this.file && this.file.path ? this.file.path : fullMatch | ||
, trim = this.config.trim || '' | ||
, basePath = this.config.basePath || '' | ||
, opts = {} | ||
|
||
if (fn.indexOf(basePath) !== 0) { | ||
throw new Error("Unable to handle " + basePath + " for " + fn); | ||
} | ||
|
||
if (optString) { | ||
// The slash replacement here is so we don't choke on example | ||
// loggers in comments. | ||
opts = new Function("return "+optString.replace(/^\/\//mg,''))(); // eslint-disable-line no-new-func | ||
} | ||
|
||
opts.name = getName (fn, opts, trim, basePath); | ||
opts.color = getColor (opts); | ||
|
||
return JSON.stringify(opts); | ||
} | ||
|
||
/** | ||
* Gets the name of a logger from its filepath. | ||
* @example | ||
* getName( | ||
* 'my-component', | ||
* { label: 'sub' }, | ||
* 'my-project.src', | ||
* 'my-project/src/components/my-component.js' | ||
* ) // returns "components.my-component" | ||
* @param {String} fn filename | ||
* @param {Object} opts { label: 'Optional logger label' } | ||
* @param {String} trim The leading portion of the name to remove. | ||
* @param {String} basePath The path to the file | ||
* @return {String} The logger name, e.g. my-project.components.my-component | ||
*/ | ||
var getName = function(fn, opts, trim, basePath){ | ||
var slashPattern = isWindows | ||
?/\\/g | ||
:/\//g | ||
var name = fn.substring(basePath.length+trim.length, fn.length) | ||
.replace(/\.jsx?$/, '') | ||
.replace(slashPattern,'.') | ||
if (opts.label) { | ||
name += '.'+opts.label | ||
} | ||
return name; | ||
} | ||
|
||
/** | ||
* Gets isomorphic color objects from filenames. | ||
* @param {String} filename | ||
* @return {Object} An isomorphic color object in the form {"color":{"server":147,"client":"rgb(127,127,212)"}} | ||
*/ | ||
var getColor = (function(){ | ||
|
||
// ANSI escape sequence on the server. | ||
// CSS rgb(...) color in the browser. | ||
var makeColor = function(r,g,b){ | ||
return { | ||
server: 16 + r*36 + g*6 + b, | ||
client: "rgb("+[ | ||
(r*42.5)|0, | ||
(g*42.5)|0, | ||
(b*42.5)|0, | ||
].join(',')+")", | ||
} | ||
} | ||
|
||
// This produces a list of 24 colors that are distant enough from each | ||
// other to be visually distinct. It's also, conveniently, the same | ||
// palette client side and server side. | ||
var colors = []; | ||
for (var r = 1; r < 6; r+=2) { | ||
for (var g = 1; g < 6; g+=2) { | ||
for (var b = 1; b < 6; b+=2) { | ||
if (r !== g || g !== b) { // No gray. | ||
colors.push(makeColor(r,g,b)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
// Just want a fairly well distributed deterministic mapping. | ||
// | ||
// Adapted from: | ||
// http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery | ||
var hash = function(str){ | ||
var hash = 0, i, chr, len; | ||
if (str.length === 0) return hash; | ||
for (i = 0, len = str.length; i < len; i++) { | ||
chr = str.charCodeAt(i); | ||
hash = ((hash << 5) - hash) + chr; | ||
hash |= 0; // Convert to 32bit integer | ||
} | ||
|
||
len = colors.length; | ||
|
||
// Positive mod. | ||
return (hash%len+len)%len; | ||
} | ||
|
||
return function(opts){ | ||
return colors[hash(opts.name)]; | ||
} | ||
})(); |
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,30 @@ | ||
{ | ||
"name": "react-server-module-tagger", | ||
"version": "0.0.1", | ||
"description": "calculates module level config", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "ava test.js", | ||
"clean": "del index.js npm-debug.log*" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+ssh://git@github.com/redfin/react-server.git" | ||
}, | ||
"keywords": [ | ||
"react-server" | ||
], | ||
"author": "Doug Wade <doug.wade@redfin.com>", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/redfin/react-server/issues" | ||
}, | ||
"homepage": "https://github.com/redfin/react-server#readme", | ||
"devDependencies": { | ||
"ava": "^0.15.2", | ||
"babel-cli": "^6.9.0", | ||
"babel-preset-es2015": "^6.9.0", | ||
"babel-preset-stage-0": "^6.5.0", | ||
"del-cli": "^0.2.0" | ||
} | ||
} |
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,32 @@ | ||
import test from 'ava'; | ||
import loggerSpec from '.'; | ||
|
||
test('creates a module tag', t => { | ||
const expected = '{\"name\":\"foo.bar\",\"color\":{\"server\":73,\"client\":\"rgb(42,127,127)\"}}'; | ||
|
||
const file = 'foo/bar'; | ||
const config = {}; | ||
const actual = loggerSpec.bind({file, config})(file); | ||
|
||
t.is(expected, actual); | ||
}); | ||
|
||
test('trims prefix from module tag name', t => { | ||
const expected = '{\"name\":\"quux\",\"color\":{\"server\":229,\"client\":\"rgb(212,212,127)\"}}'; | ||
|
||
const file = 'baz/quux'; | ||
const config = { trim: 'baz.' }; | ||
const actual = loggerSpec.bind({file, config})(file); | ||
|
||
t.is(expected, actual); | ||
}); | ||
|
||
test('adds labels', t => { | ||
const expected = '{\"label\":\"foo\",\"name\":\"has.label.foo\",\"color\":{\"server\":131,\"client\":\"rgb(127,42,42)\"}}'; | ||
|
||
const file = 'has/label'; | ||
const config = {}; | ||
const actual = loggerSpec.bind({file, config})(file, '({label: "foo"})'); | ||
|
||
t.is(expected, actual); | ||
}); |