I'm developing using node.js, and instead of writing css would like to write SCSS files that auto-compile whenever I refresh the page.
How can I get SASS files to autocompile when using NodeJS, Express and node-sass.
Update (7 Dec 2014)
The connect middleware from node-sass has been extracted into node-sass-middleware, see this answer
Install node-sass
In you project folder run:
$ npm install node-sass
Modify app.js
Assuming you have generated your app using
$ express my_app
Your app.js should look somewhat like this:
var express = require('express'),
routes = require('./routes');
var app = module.exports = express.createServer();
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
....
Here are the modifications:
var express = require('express'),
routes = require('./routes'),
sass = require('node-sass'), // We're adding the node-sass module
path = require('path'); // Also loading the path module
var app = module.exports = express.createServer();
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
// notice that the following line has been removed
// app.use(express.static(__dirname + '/public'));
// adding the sass middleware
app.use(
sass.middleware({
src: __dirname + '/sass',
dest: __dirname + '/public',
debug: true,
})
);
// The static middleware must come after the sass middleware
app.use(express.static( path.join( __dirname, 'public' ) ) );
});
It is important to note that
app.use( sass.middleware ... );
must come before
app.use( express.static ... )
The reason is that we first want sass to compile any sass files that has changed, only then serve them (which is done by express.static).
Restart your app
You'd have to restart your app in order for these changes to take place.
Include it somewhere so it compiles
We can now include app.scss in our /sass folder. But it won't compile just yet. The sass middleware will only compile files that are requested by your applications, so we must include the (to be rendered) css file somewhere, like in `/views/layout.jade':
doctype html
html(lang="en")
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
link(rel="stylesheet", href="app.css") < we've added this
body!= body `/views/layout.jade`
Notice that unlike style.css which is under the stylesheets sub-folder, the app.css is read from the root (in this case /public).
Fixing paths
With
app.use(
sass.middleware({
src: __dirname + '/sass',
dest: __dirname + '/public',
debug: true,
})
);
After the first compilation, the file and folder hierarchy will look like so:
Project folder
app.js
public
app.css < This is where the compiled file goes
sytlesheets
style.css
sass
app.scss < This is where the sass file is
You may want to have the compiled output in the stylesheets folder, rather than the public one; like so:
Project folder
app.js
public
sytlesheets
app.css
style.css
sass
app.scss
This way, the view will link to the css files like so:
link(rel='stylesheet', href='/stylesheets/style.css')
link(rel="stylesheet", href="/stylesheets/app.css")
However, if you change the sass middleware configuration to
app.use(
sass.middleware({
src: __dirname + '/sass',
dest: __dirname + '/public/stylesheets',
debug: true,
})
);
things will go pear shape.
When linking to the css file like so:
link(rel="stylesheet", href="stylesheets/app.css")
the resultant request will be for stylesheets/app.css. But since we gave the sass middleware the following config:
src: __dirname + '/sass',
it will go and look for /sass/stylesheets/app.scss, and no such file exists.
One solution is to keep the config as is, but the put all sass files in the subfolder `/sass/stylesheets/. But there is a better solution.
If you define a prefix config like so:
app.use(
sass.middleware({
src: __dirname + '/sass',
dest: __dirname + '/public/stylesheets',
prefix: '/stylesheets', // We've added prefix
})
);
it will tell the sass compiler that request file will always be prefixed with /stylesheets and this prefix should be ignored, thus for a request for /stylesheets/app.css, the sass middleware will look for the file /sass/app.scss rather than /sass/stylesheets/app.scss.
Final code
app.js
var express = require('express'),
routes = require('./routes'),
sass = require('node-sass'),
path = require('path');
var app = module.exports = express.createServer();
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(
sass.middleware({
src: __dirname + '/sass',
dest: __dirname + '/public/stylesheets',
prefix: '/stylesheets',
debug: true,
})
);
app.use(express.static(path.join(__dirname, 'public')));
});
layout.jade
doctype html
html(lang="en")
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
link(rel="stylesheet", href="/stylesheets/app.css")
body!= body
Folders and files
Project folder
app.js
public
sytlesheets
app.css
style.css
sass
app.scss
The connect middleware from node-sass has been extracted into node-sass-middleware. Use as follows:
var fs = require('fs'),
path = require('path'),
express = require('express'),
sassMiddleware = require('node-sass-middleware')
var app = module.exports = express();
// adding the sass middleware
app.use(
sassMiddleware({
src: __dirname + '/sass',
dest: __dirname + '/src/css',
debug: true,
})
);
// The static middleware must come after the sass middleware
app.use(express.static(path.join(__dirname, 'public')));
Related
I want to build a simple app using node.js that 'functions' like IIS/classic ASP where all the content (html, png, js, css, ejs) can be in one directory and the ejs file uses javascript vs. VBScript.
I've gathered the following from API's and other examples, but the ejs file arrives at the browser as a binary and gets saved....
My node.js file:
var express = require('express');
var app = express();
app.use(app.router);
app.use(express.static(__dirname + '/html'));
app.engine('.ejs', require('ejs').__express);
app.set('views', __dirname + '/html');
app.set('view engine', 'ejs');
app.get('*.ejs', function(req, res) {
res.render(__dirname + '/html' + req.url, function(err, result) {
res.end(result);
});
});
app.listen(8080);
My test.ejs file:
<!DOCTYPE html>
<html>
<head><title>A test</title></head>
<body>
My Test Page
<% if (1==1) { %>
Working
<%}%>
</body>
</html>
What am I missing or where can I find a working example?
I FIGURED IT OUT... SOLUTION ADDED ABOVE
express.js won't magically try to render static files, you have to explicitly call render somewhere.
I believe something like this would work:
var express = require('express')
var app = express()
app.engine('.ejs', require('ejs').__express)
app.set('views', __dirname + '/html')
app.set('view engine', 'ejs')
app.use(app.router) // only in express 3, remove that in express 4
app.get('/test.html', function(req, res, next) {
res.render('test.ejs')
})
app.use(express.static(__dirname + '/html'))
app.listen(8080)
I believe is not necessary to run render in every place, I use this example to render my static content by migrating express 2.3.7 to 4 and works fine with :
app.engine('.ejs', require('ejs').__express);
app.set('views', __dirname + '/public')
app.set('view engine', 'ejs')
app.use(express.static(__dirname + '/public'))
My static content is in 'public' directory.
I just want to add that express supports EJS out of the box these days:
https://expressjs.com/en/starter/generator.html
My node.js app can't find the public folder. I've tried using connect static and express static from this answer: static files with express.js and also this answer: File not found in Node.js
But I still can't get it working. I get the content of the page I try to display, but not any of the links to css and js files.
Any ideas?
This is my app.js file.
var express = require('express'),
post = require('./routes/posts'),
web = require('./routes/web'),
http = require('http'),
stylus = require('stylus'),
nib = require('nib'),
path = require('path');
var app = express();
function compile(str, path) {
return stylus(str).set('filename', path).use(nib());
}
app.configure(function () {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart());
app.use(app.router);
app.use(stylus.middleware({src: __dirname + '/public', compile: compile}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.bodyParser({ keepExtensions: true, uploadDir: __dirname + '/upload/photos' }));
});
//app.use(app.router);
//Web
app.get('/', web.frontPage);
app.post('/posts', post.findAllPosts);
app.post('/posts/:id', post.findPostById);
app.post('/postAdd', post.addPost);
app.put('/posts/:id', post.updatePost);
app.delete('/posts/:id', post.deletePost);
app.get('*', function(req, res){
res.render('404');
});
http.createServer(app).listen(80);
console.log('Listening on port 80...');
Your problem is caused by this:
app.get('*', function(req, res){
res.render('404');
});
Because you're declaring the app.router middleware (which handles that catch-all route) before the static middleware, the router gets all the requests for static files too, and if they aren't handled by any of your other routes, it will generate a 404.
The solution would be to move the static middleware to before the router. The same also needs to be applied to the Stylus middleware and the bodyParser middleware, to make sure they are called before your routes:
app.use(stylus.middleware({src: __dirname + '/public', compile: compile}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.bodyParser({ keepExtensions: true, uploadDir: __dirname + '/upload/photos' }));
app.use(app.router);
(although it seems that you can skip the bodyParser middleware, since you're using the json, urlencoded and multipart middlewares already; the bodyParser middleware is just a very thin wrapper around those three)
I have an existing coded frontend i.e views,javascripts,stylesheets (https://github.com/stdrunk/Taskr) and I intend to add this to the express framework so that i can link it to the db.
I added the contents to the public folder. The javascripts in the javascript folder, css in stylesheets, and images in images folder.
Then i changed the code of app.js according to this Render basic HTML view?
Now when run app.js and open the page in the browser i get a stripped version of my original page.
No error comes in the console.
This is my app.js
/**
* Module dependencies.
*/
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
//app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.use(express.static(__dirname + '/public'));
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.index);
app.get('/users', user.list);
app.get('/home', function (req, res)
{
res.render('index.html');
});
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
You could put all those dirs under a 'public' dir, and then use:
app.use(express.static(__dirname + '/public'));
That way, express will always just send anything requested from those directories, and you won't need to worry about static files at all.
Although, I do recommend keeping something like Apache running on your server to serve static files. Images especially.
Using node (0.10.15) and express (3.3.4), along with the latest node-sass, and I can't get my scss files to compile. My app.js file looks like this:
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, sass = require('node-sass');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.use(
sass.middleware({
src: __dirname + '/public/sass',
dest: __dirname + '/public',
debug: true,
outputStyle: 'compressed'
})
);
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.index);
app.get('/users', user.list);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
What am I missing to get sass to auto-compile?
If it matters, I'm using supervisor to monitor changes.
Middlewares are executed in the order they are attached to the app. The node-sass middleware only compiles scss files to css, it does not serve them. The static middleware is able to serve the compiled css files, however, it can’t do so as long as the css files are not compiled yet. If you switch static and sass middlewares, everything should work as expected:
app.use(
sass.middleware({
src: __dirname + '/public/sass',
dest: __dirname + '/public',
debug: true,
outputStyle: 'compressed'
})
);
app.use(express.static(path.join(__dirname, 'public')));
Now the following happens when you request /style.css:
sass notices a request for a css file. If the dest file (__dirname + '/public' + '/style.css') is up-to-date, it does nothing. Otherwise, it looks for the src file (__dirname + '/public/sass' + '/style.scss') and compiles it, if it exists.
static notices a request for a static asset. If the requested file (path.join(__dirname, 'public') + '/style.css') does exist, it is served.
I also had serious problems setting this up with Koa (koa.js) and koa-sass. Eventually I figured out the problems. node-sass is 'too clever' for it's own good:
app.use(sass({
src: __dirname + '/public/sass',
dest: __dirname + '/public/css',
debug: true,
outputStyle: 'compressed',
prefix: '/stylesheets'
}));
I was getting a 500 when it tried to access /public/sass/stylesheets/styles.css- but I didn't have 'stylesheets' in my path: My html template file was looking at /stylesheets/styles.css so I had to remove the prefix with prefix: '/stylesheets'.
Next I had a problem with the scss file - I had accidentally copied an old .styl file and it was trying to use nib - which I found after discovering the debug: true setting.
As soon as I replaced it with a valid css or scss file it compiled and rendered using koa-static.
Although this is a koa-sass issue, koa-sass just wraps node-sass with a generator so it belongs here (IMO).
I just started using node and I'm trying to get it up and running with an express jade and stylus package. I think I have it set up correctly but the CSS files are not being compiled and I keep getting a 404 not found response when the request for the stylesheet is made.
Here is my server
/*
* Module dependencies
*/
var express = require('express'),
stylus = require('stylus'),
nib = require('nib');
var app = express();
function compile(str, path) {
return stylus(str)
.set('filename', path)
.use(nib());
}
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger('dev'));
app.use(stylus.middleware({
src: __dirname + '/public/stylesheets',
compile: compile
}));
app.use(express.static(__dirname + '/public'));
/* Routes */
app.get('/', function (req, res) {
res.render('index',
{ title : 'Home' }
)
})
/* Start the server */
app.listen(3000);
console.log('listening on port 3000');
and my layout.jade File
!!!5
html
head
title #{title} - My Site
link(rel='stylesheet', href='stylesheets/style.css')
body
header
h1 My Site
.container
.main-content
block content
.sidebar
block sidebar
footer
p Running on node with Express, Jade and Stylus
Figured it out
https://github.com/LearnBoost/stylus/issues/122
a request to /css/style.css interprets to /src/css/style.styl