SSR React - Load Class Names Before bundle.js loads - node.js

I am attempting to create a SSR react app with Firebase hosting and Cloud Functions. My components are using className to declare classes. My server-rendered html does not include these, it only has the data-reactid elements.
It is not until the bundle.js is loaded that the real class="example-class" is loaded.
I do not want to wait for the bundle.js to download before the classes are loaded. I'd rather not code with both
class="kitten-image" className="kitten-image"
because that seems like a waste. I have not been able to find anything that either transforms the CSS files to have the data-reactid identifiers, or to automatically include the class="kitten-image" on the server-side during the compile process with babel.
Overview: My server-side compiled code injects the babel compiled react components into an index.html template file, which is sent via express app on http request on Firebase functions. The index.html file includes hard-coded references to the webpack processed styles.css and bundle.js in the firebase hosting public folder.
Thus, my server-side rendered HTML should immediately be able to reference the styles.css sheet - however, the classes are not in the html until the bundle.js is loaded (which is the problem).
Server-side rendered HTML before bundle.js loads
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React Server Side Rendering - Firebase Hosting</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root"><div data-reactroot="" data-reactid="1" data-react-checksum="1473597379"><h1 data-reactid="2">Hello World!</h1><p data-reactid="3"><!-- react-text: 4 -->This is a kitten: <!-- /react-text --><br data-reactid="5"/><img src="/media/kitten.jpg" alt="Kitten" data-reactid="6"/></p></div></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
HTML after bundle.js loads
Note that class="kitten-image" has been added.
<!DOCTYPE html>
<html><head>
<meta charset="UTF-8">
<title>React Server Side Rendering - Firebase Hosting</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root"><div data-reactroot="" data-reactid="1"><h1 data-reactid="2">Hello World!</h1><p class="intro" data-reactid="3"><!-- react-text: 4 -->This is a kitten: <!-- /react-text --><br data-reactid="5"><img src="/media/kitten.jpg" alt="Kitten" class="kitten-image" data-reactid="6"></p></div></div>
<script type="text/javascript" src="bundle.js"></script>
</body></html>
Folder Structure
App Component Example
see className
import React, { Component } from 'react';
import kitten from "./kitten.jpg";
import "./App.scss";
class App extends Component {
render() {
return (
<div class="main">
<h1>Hello World!</h1>
<p className="intro">This is a kitten: <br /><img src={kitten} alt="Kitten" className="kitten-image" /></p>
</div>
);
}
}
export default App;
Babel Compiled Component
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var kitten = "/media/kitten.jpg";
var App = function (_Component) {
_inherits(App, _Component);
function App() {
_classCallCheck(this, App);
return _possibleConstructorReturn(this, (App.__proto__ || Object.getPrototypeOf(App)).apply(this, arguments));
}
_createClass(App, [{
key: "render",
value: function render() {
return _react2.default.createElement(
"div",
{ "class": "main" },
_react2.default.createElement(
"h1",
null,
"Hello World!"
),
_react2.default.createElement(
"p",
null,
"This is a kitten: ",
_react2.default.createElement("br", null),
_react2.default.createElement("img", { src: kitten, alt: "Kitten" })
)
);
}
}]);
return App;
}(_react.Component);
exports.default = App;
Server index.js
import React from "react";
import { renderToString } from "react-dom/server";
import App from "../shared/App";
import express from "express";
import * as fs from "fs";
import * as functions from "firebase-functions";
const index = fs.readFileSync(__dirname + '/../../index.template.html', 'utf8');
const app = express();
app.get('**', (req, res) => {
const html = renderToString(<App />);
const finalHtml = index.replace('<!-- ::APP:: -->', html);
res.set('Cache-Control', 'public, max-age=600, s-maxage=1200');
res.send(finalHtml);
});
export let ssrapp = functions.https.onRequest(app);
//app.listen(3006, () => { console.log('Listening on 3006.'); });
Server index.js babel compiled
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ssrapp = undefined;
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _server = require("react-dom/server");
var _App = require("../shared/App");
var _App2 = _interopRequireDefault(_App);
var _express = require("express");
var _express2 = _interopRequireDefault(_express);
var _fs = require("fs");
var fs = _interopRequireWildcard(_fs);
var _firebaseFunctions = require("firebase-functions");
var functions = _interopRequireWildcard(_firebaseFunctions);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var index = fs.readFileSync(__dirname + '/../../index.template.html', 'utf8');
var app = (0, _express2.default)();
app.get('**', function (req, res) {
var html = (0, _server.renderToString)(_react2.default.createElement(_App2.default, null));
var finalHtml = index.replace('<!-- ::APP:: -->', html);
res.set('Cache-Control', 'public, max-age=600, s-maxage=1200');
res.send(finalHtml);
});
var ssrapp = exports.ssrapp = functions.https.onRequest(app);
//app.listen(3006, () => { console.log('Listening on 3006.'); });
Webpack
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const autoprefixer = require("autoprefixer");
const extractSass = new ExtractTextPlugin({
filename: "public/styles.css",
disable: process.env.NODE_ENV === "development"
});
// Webpack settings unique to browser-side script
const browserConfig = {
entry: './src/browser/index.js',
devtool: "source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: [/\.svg$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: "file-loader",
options: {
name: "public/media/[name].[ext]",
publicPath: url => url.replace(/public/, "")
}
},
{
test: /\.scss$/,
use: extractSass.extract({
use: [
{ loader: 'css-loader', options: { sourceMap: true } },
{
loader: 'postcss-loader',
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
sourceMap: true,
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
{ loader: 'sass-loader', options: { sourceMap: true } }
],
// use style-loader in development
fallback: "style-loader"
})
}
]
},
plugins: [
extractSass
],
output: {
filename: './public/bundle.js',
path: __dirname
}
}
module.exports = [browserConfig];

I was initially using Babel because I was having issues compiling via webpack with the Google Cloud / firebase modules. It was trying to bundle in everything unnecessarily.
I created a separate webpack server-side configuration. This does a few things to work correctly.
1.I'm using the "webpack-node-externals" package which is designed to exclude node modules for backend compilation. Without this, my generated JS file was enormous. My backend has a whole node_modules folder, so it does not need these items bundled.
2.I added the false statements to __dirname and __filename - I don't know what this does or how it works, but it fixed my issue with opening and reading my html template file server-side.
3.The file-loader does not actually copy the files, with emit: false
The Real Fix: The .scss tester uses css-loader/locals This was key! It generates the correct class names on the server and places them in the components when rendering! It also does not bundle / copy and files in this configuration, since the browser side config does that.
I was using the following plugin in my .babelrc file with the babel compile method. This breaks the image / file transfer process in webpack and must be removed from .babelrc
"plugins": [["transform-assets-import-to-string", {
"baseDir": "",
"baseUri": "/media"
}]],
Revised Webpack
const serverConfig = {
entry: "./src/server/index.js",
target: "node",
externals: [nodeExternals()], // exclude node_modules
node: {
__dirname: false,
__filename: false
},
output: {
filename: "./functions/src/server/server.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{
test: [/\.svg$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: "file-loader",
options: {
name: "public/media/[name].[ext]",
publicPath: url => url.replace(/public/, ""),
emit: false
}
},
{
test: /\.scss$/,
use: [
{ loader: 'css-loader/locals' },
{ loader: 'sass-loader' }
]
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};

Related

Error development is not defined error with react-hot-loader v3 and webpack-hot-middleware

Given that these are the dependencies that I am using:
"react-hot-loader": "3.0.0-beta.7",
"webpack": "2.6.1",
"webpack-dev-middleware": "^1.11.0",
"webpack-hot-middleware": "^2.18.1",
"webpack-merge": "^4.1.0"
Error
patch.js:5
Uncaught ReferenceError: development is not defined
at Object.defineProperty.value (patch.js:5)
at __webpack_require__ (bootstrap 921586e…:659)
at fn (bootstrap 921586e…:85)
at Object.options.path (patch.js:1)
at __webpack_require__ (bootstrap 921586e…:659)
at fn (bootstrap 921586e…:85)
at Object.<anonymous> (process-update.js:132)
at __webpack_require__ (bootstrap 921586e…:659)
at validateFormat (bootstrap 921586e…:708)
at bootstrap 921586e…:708
You might want to either take a look at this repo

webpack-config
const FILE_PATHS = {
entry: path.resolve('./src/index.js'),
reactHotLoader: 'react-hot-loader/patch',
hmrEntry: 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000', // this is from the webpack-hot-middleware docs
output: '/' // this is the path used by webpack-dev-middleware, the docs say no real path is required, just pass in `/`
}
const devOnly = {
entry: FILE_PATHS.entry,
output: {
path: '/',
publicPath: '/assets/',
filename: 'bundle.js'
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.jsx?$/,
use: [
{
loader: 'babel-loader'
}
],
// react-hot-loader asks to include src and exclude node_modules in https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md
include: path.resolve('./src'),
exclude: /node_modules/
},
{
test: /\.json$/,
use: [
{
loader: 'json-loader'
}
]
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-sprite-loader'
}
]
}
]
},
plugins: [
new DefinePlugin({
'process.env.NODE_ENV': 'development'
})
]
}
const hmr = {
entry: [FILE_PATHS.reactHotLoader, FILE_PATHS.hmrEntry, FILE_PATHS.entry],
plugins: [new HmrPlugin(), new NoErrorsPlugin()],
devServer: {
hot: true
}
}
const dev = merge(devOnly, hmr)
module.exports = {dev}
Express Server
// process.env.NODE_ENV = 'development'
const express = require('express')
const webpack = require('webpack')
const historyApiFallback = require('connect-history-api-fallback')
const normalizeAssets = assets => {
return Array.isArray(assets) ? assets : [assets]
}
const getLinks = assets => {
const styles = assets.filter(path => path.endsWith('.css'))
const links = styles.map(path => `<link rel="stylesheet" href="${path}" />`)
return links.join('\n')
}
const publicPath = '/assets/'
const getScripts = assets => {
const js = assets.filter(path => path.endsWith('.js'))
const scripts = js.map(path => `<script src="${path}"></script>`)
return scripts.join('\n')
}
const devMiddlewareConfig = {
serverSideRender: true,
stats: 'normal',
publicPath: publicPath,
watchOptions: {
poll: 1000,
aggregateTimeout: 300
}
}
const hotMiddlewareConfig = {
reload: true,
overlay: true
}
const devMiddlewareCreator = require('webpack-dev-middleware')
const hotMiddlewareCreator = require('webpack-hot-middleware')
const options = require('./webpack.config')
const {dev: devConfig} = options
const compiler = webpack(devConfig)
const devMiddleware = devMiddlewareCreator(compiler, devMiddlewareConfig)
const hotMiddleware = hotMiddlewareCreator(compiler, hotMiddlewareConfig)
const app = express()
app.use(devMiddleware)
app.use(hotMiddleware)
app.use(express.static(__dirname + '/public'))
app.use((req, res) => {
const stats = res.locals.webpackStats.toJson()
const assets = normalizeAssets(stats.assetsByChunkName.main)
const styles = getLinks(assets)
const scripts = getScripts(assets)
res.send(
`
<!DOCTYPE html>
<html>
<head>
<title>Webpack is crazy</title>
${styles}
</head>
<body>
<div id="app">
</div>
${scripts}
</body>
</html>
`
)
})
// app.use(historyApiFallback)
app.listen(3000, err => {
if (!err) {
console.log('Server is listening on port 3000')
}
})
The error is caused by this part of your configuration:
new DefinePlugin({
'process.env.NODE_ENV': 'development'
})
To quote the documentation:
Note that because the plugin does a direct text replacement, the value given to it must include actual quotes inside of the string itself. Typically, this is done either with either alternate quotes, such as '"production"', or by using JSON.stringify('production').
If you replace it with, for example, the following, it should work:
new DefinePlugin({
'process.env.NODE_ENV': '"development"'
})

React code not compiling with webpack

My code wont compile for some reason. The weird thing is that is will work when I use the in browser interpreter.
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom#15/dist/react-dom.min.js"></script>
React code:
var Cookies = require('cookies');
var cookieParser = require('cookie-parser');
var name = document.getElementById('name').innerHTML;
//var name = req.user.name;
var start = false;
var Assets = React.createClass({
getInitialState: function(){
return({
assets: [],
secondsElapsed: 0
});
},
tick: function() {
//this.setState({secondsElapsed: this.state.secondsElapsed + 1});
if(start === true){
console.log(name);
var myHeaders = new Headers();
var token = new Cookies(req,res).get('access_token');
myHeaders.append('acess_token', token);
var myInit = { method: 'GET',
headers: myHeaders};
fetch('/api/user/all/?name='+name, myInit).then(function(data){
return data.json();
}).then( json => {
this.setState({
assets: json
});
});
}
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function(){
var assets = this.state.assets;
assets = assets.map(function(asseti,index){
return(
asseti.map(function(asset, index){
return(
<li key={index}>
<span className={asset.active}></span>
<span>{asset.name}</span>
<span >{asset.description}</span>
<span>{asset.location.coordinates[0]}{asset.location.coordinates[1]}</span>
</li>
)
})
)
});
return(
<div>
<form onSubmit={this.handleSubmit}>
<input type="submit" value="Find assets" />
</form>
{assets}
</div>
);
},
handleSubmit: function(e){
e.preventDefault();
start = true;
// name = this.refs.name.value;
fetch('/api/user/all/?name='+name).then(function(data){
return data.json();
}).then( json => {
this.setState({
assets: json
});
});
}
});
ReactDOM.render(<Assets />, document.getElementById('assets'));
Webpack.config.js:
var path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'puplic') + '\\js\\baseReact.js',
output: {
path: path.resolve(__dirname, 'dist') + '/app',
filename: 'bundle.js',
publicPath: '/app/'
},
module: {
loaders: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'public/js'),
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0']
}
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}
]
},
devServer: {
historyApiFallback: true
}
};
Error:
ERROR in ./puplic/js/baseReact.js
Module parse failed: C:\Users\test\Documents\GPSTracker\puplic\js\baseReact.js Unexpected token (
53:14)
You may need an appropriate loader to handle this file type.
| asseti.map(function(asset, index){
| return(
| <li key={index}>
| <span className={asset.active}></span>
| <span>{asset.name}</span>
I figure I must be doing something dumb like missing something as it runs in browser which is weird. Does that gloss over some errors as it is interpreted as opposed to been compiled before been run?
Based on the comments, you may be missing the fetch import. Fetch is not readily available in all browsers.
The npm package whatwg-fetch mentions specifically how to get fetch working in a webpack-enabled environment.
Installation
npm install whatwg-fetch --save
or
bower install fetch.
You will also need a Promise polyfill for older browsers. We recommend
taylorhakes/promise-polyfill for its small size and Promises/A+
compatibility.
For use with webpack, add this package in the entry configuration
option before your application entry point:
entry: ['whatwg-fetch', ...]
For Babel and ES2015+, make sure to
import the file (in your react-components):
import 'whatwg-fetch'
Also, looking at your code, which is deviating a tad from regular javascript style guides in terms of spacings, I'd look into getting eslint up and running in your environment for better feedback for errors like these. If you had eslint enabled, you'd get fetch is undefined as soon as you tried something like this without importing fetch first.
Another personal note from me, try just importing whatwg-fetch in your file, before tampering with your webpack config. You may not need to add it as an entry.
Best of luck!

System.js builder.buildStatic just outputs the filename

I have just simplified the source file to below, which works fine when I use System.import in a script tag.
import angular from "angular";
angular.element(document).ready(function () {
alert('Hello word');;
});
Below is my config.js
System.config({
baseURL: "/",
defaultJSExtensions: true,
transpiler: "typescript",
paths: {
"npm:": "jspm_packages/npm/"
},
map: {
"angular": "npm:angular#1.5.2",
"typescript": "npm:typescript#1.8.9"
}
});
I have a gulp task to build it:
gulp.task('bundle:js', ['clean:js'], function () {
var builder = new Builder();
builder.loadConfig('./config.js').then(function () {
var destination = paths.coreJsDest;
builder.buildStatic(paths.srcRoot + 'js/ng/homepage', destination, {
minify: false,
sourceMaps: false,
runtime: false
});
});
});
But the output file contains the file name rather than JavaScript from the file and its imports:
(["1"], ["1"], function($__System) {
})
(function(factory) {
if (typeof define == 'function' && define.amd)
define(["D:/Projects/etc-etc/assets/js/ng/homepage.js"], factory);
else if (typeof module == 'object' && module.exports && typeof require == 'function')
module.exports = factory(require("D:/Projects/etc-etc/js/ng/homepage.js"));
else
throw new Error("Module must be loaded as AMD or CommonJS");
});
Doh, I just needed to change the baseUrl from "/" to "./".

Webpack externals in both node and the browser

I have an isomorphic React application which runs in both the browser and on the server. I build the same code for both by running two separate Webpack builds through two different entry points and with different configs.
The problem is that the external file that exists on the browser window via an external script tag (Google Maps in this instance) obviously won't exist when running in node on the server. The code is identical except the entry point file.
index.html:
// index.html
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=XXX"></script>
Simplified config:
// Server Webpack config
{
entry: 'server.js',
target: 'node',
externals: {
google: google
}
}
// Client Webpack config
{
entry: 'client.js',
target: 'browser',
externals: {
google: google
}
}
The Component:
// The view which builds and runs fine in
// the client but doesn't run on the server.
var React = require('react'),
css = require('./style.css'),
google = require('google'); // Nope, not on the server obviously!
var Component = React.createClass({
render: function () {
return (
<div>
// Do maps stuff
</div>
);
}
});
module.exports = Component;
My question is how should I handle this?
Error: Cannot find module 'google'
I currently have a solution which I'm not at all keen on.
// Server Webpack config
{
entry: 'server.js',
target: 'node',
externals: {
google: google
},
plugins: [
new webpack.DefinePlugin({ 'ENV.browser': false }),
]
}
// Client Webpack config
{
entry: 'client.js',
target: 'browser',
externals: {
google: google
},
plugins: [
new webpack.DefinePlugin({ 'ENV.browser': true }),
]
}
// The component
var React = require('react'),
css = require('./style.css');
if (ENV.browser) {
var google = require('google');
}
var Component = React.createClass({
render: function () {
return (
<div>
if (ENV.browser) {
// Do maps stuff
}
</div>
);
}
});
module.exports = Component;
You can use NormalModuleReplacementPlugin to replace the module with a noop, as per an idea from Dustan Kasten:
{
plugins: [
new webpack.NormalModuleReplacementPlugin(/^google$/, 'node-noop'),
],
}

node assetmanager with more modules

I'm trying to set up assetmanager
for my blog that has three modules
default
login
admin
I tried like
assets.json
{
"css": {
"app":{
"public/src/dist/default/css/dist.min.css": [
"public/src/assets/default/css/*.css"
]
},
"login":{
"public/src/dist/login/css/dist.min.css": [
"public/src/assets/default/css/*.css"
]
},
"admin":{
"public/src/dist/admin/css/dist.min.css": [
"public/src/assets/admin/css/*.css"
]
}
}
}
express.js
assetmanager.init({
js: assets.js,
css: assets.css,
debug: (process.env.NODE_ENV !== 'production'),
webroot: 'public'
});
// Add assets to local variables
app.use(function(req, res, next) {
res.locals({
assets: assetmanager.assets
});
next();
});
console.log(assetmanager.assets);
but console.log(assetmanager.assets);
give me a empty array []
so is there a way to manage assetmanager
with more than one module ?
the best way I found up to now
is like in my controllers:
'use strict';
var assetmanager = require('assetmanager');
exports.render = function(config) {
var assets = require(config.sroot+'/config/assets.json');
assetmanager.init({
js: assets.js.app,
css: assets.css.app,
debug: (process.env.NODE_ENV !== 'production'),
webroot: 'public'
});
return function(req, res) {
res.render('layouts/default', {appTitle:'ilwebdifabio',assets:assetmanager.assets});
}
};
but it's quite ugly and I have
duplicate code :(
END UP
There is no way to use assetmanager module
in different modules (login,default,admin).
Modules are automatically cached by the Node.js application upon first load. As such, repeated calls to require() - the global method that loads modules - will all result in a reference to the same cached object.
so you end up ie if you use in a module
to the have the dedicate assets in all other module so
I worked it out with :
'use strict';
var _ = require('lodash');
module.exports = function (path,route) {
var env = (process.env.NODE_ENV === 'production') ? 'production' : null;
var debug = (env !== 'production');
var data = require(path+'/config/assets.json');
var assets = {
css: [],
js: []
};
var getAssets = function (pattern) {
var files = [];
if (_.isArray(pattern)) {
_.each(pattern, function (path) {
files = files.concat(getAssets(path));
});
} else if (_.isString(pattern)) {
var regex = new RegExp('^(//)');
if (regex.test(pattern)) {
// Source is external
//For the / in the template against 404
files.push(pattern.substring(1));
} else {
files.push(pattern);
}
}
return files;
};
var getFiles = function () {
var current = data[route];
_.each(['css', 'js'], function (fileType) {
_.each(current[fileType], function (value, key) {
if (!debug) {
assets[fileType].push(key);
} else {
assets[fileType] = assets[fileType].concat(getAssets(value));
}
});
});
};
var getCurrentAssets = function(){
return assets;
};
getFiles();
return {
getCurrentAssets: getCurrentAssets
};
};
in the controller
var assetmanager = require(config.sroot+'/utils/assetsmanager')(config.sroot,'app');
res.render('layouts/default', {
assets:assetmanager.getCurrentAssets()
});
There is a new version of assetmanager 1.0.0 that I believe accomplishes what you're trying to do more effectively. In the new version you can break apart your assets into groups so that you can support multiple layouts. The github has a complete example here but essentially your asset files ends up looking something like this:
{
"main": {
"css": {
"public/build/css/main.min.css": [
"public/lib/bootstrap/dist/css/bootstrap.css",
"public/css/**/*.css"
]
},
"js": {
"public/build/js/main.min.js": [
"public/lib/angular/angular.js",
"public/js/**/*.js"
]
}
},
"secondary": {
"css": {
"public/build/css/secondary.min.css": [
"public/css/**/*.css"
]
},
"js": {
"public/build/js/secondary.min.js": [
"public/js/**/*.js"
]
}
}
}
And then in your layouts you just include the group you want. Hopefully that helps out.

Resources