Browse Source

added transform feature

master
mortie 7 years ago
parent
commit
2047333b1a
3 changed files with 139 additions and 4 deletions
  1. 32
    0
      README.md
  2. 37
    1
      index.js
  3. 70
    3
      js/static.js

+ 32
- 0
README.md View File

@@ -106,3 +106,35 @@ Serve static files.
* `app.get("^/static/.*", webframe.static("web", "/static"))` will serve all
files in the `web` directory under `/static`, so `/static/script.js` will
serve `web/script.js`.

### Transformations - app.transform(extension, mime, function)

Webframe has a way to transform files of one file type to an other, for example
transforming typescript into javascript, or sass into CSS.

Example (assumes you have the node-sass module installed):

```
var sass = require("node-sass");

app.transform("sass", "text/css", (path, writeStream) => {
try {
var str = fs.readFileSync(path, "utf8");
} catch (err) {
ws.status = 404;
return ws.error(err);
}

try {
var result = sass.renderSync({ data: str });
writeStream.end(result.css);
} catch (err) {
writeStream.error(err);
}
});
```

Now, any .sass file served with `webframe.static` will be compiled into CSS,
and sent with the browser with the MIME type "text/css". The result is also
cached, so the expensive sass compilation will only happen the first time the
file is requested. The result will not be cached if you call writeStream.error.

+ 37
- 1
index.js View File

@@ -53,6 +53,7 @@ class App {

this._routeMap = {};
this._routes = [];
this._transforms = {};

// Add scripts
if (options.client_utils) {
@@ -135,7 +136,7 @@ class App {
* Args:
* method: "GET", "POST", "PUT", "DELETE", "ALL"
* path: path,
* func: function
* func: function(request, response)
*/
route(method, path, middleware, func) {
if (method !== "GET" && method !== "POST" && method !== "PUT" &&
@@ -185,6 +186,35 @@ class App {
}
}

/*
* Add a transform.
* Args:
* ext: What extension to transform
* mime: The mime-type the transformed code should be sent as
* func: function(path, writeStream)
*
* writeStream: {
* status: status code (should only be modified before write() or end())
* headers: HTTP headers (should only be modified before write() or end())
* write: function(data) - write data
* end: function(data) - write data and end response
* error: function(err) - end response and log error
* }
*/
transform(ext, mime, func) {
if (typeof ext !== "string")
throw new Error("Expected ext to be string, got "+(typeof ext)+".");
if (typeof func !== "function")
throw new Error("Expected func to be function, got "+(typeof func)+".");
if (this._transforms[ext])
throw new Error("Extension "+ext+" already has a transform.");

this._transforms[ext] = {
func: func,
mime: mime
};
}

/*
* Add code to be served as /webframe.js
*/
@@ -201,12 +231,18 @@ class App {
*/
template(tmpl, args) { return template(tmpl, args); }

/*
* Utility methods for GET/POST/PUT/DELETE/ALL
*/
get(path, middleware, func) { this.route("GET", path, middleware, func); }
post(path, middleware, func) { this.route("POST", path, middleware, func); }
put(path, middleware, func) { this.route("PUT", path, middleware, func); }
delete(path, middleware, func) { this.route("DELETE", path, middleware, func); }
all(path, middleware, func) { this.route("ALL", path, middleware, func); }

/*
* Logging
*/
info(str) {
console.log("Info: "+str);
}

+ 70
- 3
js/static.js View File

@@ -62,10 +62,80 @@ function sendfile(path, app, pathname, req, res) {
});
}

var transformCache = {};
function handleTransform(path, req, res, app) {
var ext = pathlib.extname(req.url).substring(1);
if (ext === "")
return false;

var transform = app._transforms[ext];
if (!transform)
return false;

if (transformCache[path]) {
var c = transformCache[path];
res.writeHead(c.status, c.headers);
res.end(c.content);
return true;
}

var data = { content: "", headers: null, status: null };

var writeStream = {
headersSent: false,
status: 200,
headers: {
"content-type": transform.mime
},

write: function(d) {
if (!writeStream.headersSent) {
res.writeHead(writeStream.status, writeStream.headers);
writeStream.headersSent = true;
}
if (d != null) {
data.content += d;
res.write(d);
}
},

end: function(d, nocache) {
writeStream.write(d);

res.end();

if (!nocache) {
data.status = writeStream.status;
data.headers = writeStream.headers;
transformCache[path] = data;
}
},

error: function(err) {
if (writeStream.status === 200)
writeStream.status = 500;

app.warning("Transform for "+path+": "+err.toString());
writeStream.end(null, true);
}
}

transform.func(path, writeStream);
return true;
}

module.exports = function(root, before) {
return function(req, res, app) {
// `req.urlobj.pathname` is too long.
var pn = req.urlobj.pathname;

// Join the web root with the request's path name
var path = pathlib.join(root, pn.replace(before, ""));

// If there's a transform, let that handle it
if (handleTransform(path, req, res, app))
return;

// Send a file
function send(path) {
sendfile(path, app, pn, req, res);
@@ -79,9 +149,6 @@ module.exports = function(root, before) {
return;
}

// Join the web root with the request's path name
var path = pathlib.join(root, pn.replace(before, ""));

fs.stat(path, (err, stat) => {

// If there's an error stat'ing, just error

Loading…
Cancel
Save