Export JSON from i18next to the client - node.js

I'm trying to export a JSON locale file from i18next to a link as an API to use on the client. The problem is that it only exports the locale that is specified in "fallbackLng", which is 'en'. How can I make it detect the locale from the "detection_options" so that the right locale is loaded and exported on the API?
// app.js
var detection_options = {
// order and from where user language should be detected
order: [/*'path', 'session', */ 'querystring', 'cookie', 'header'],
// keys or params to lookup language from
lookupQuerystring: 'lng',
lookupCookie: 'i18next',
lookupHeader: 'accept-language',
lookupSession: 'lng',
lookupPath: 'lng',
lookupFromPathIndex: 0,
// cache user language
caches: false,
}
// i18next configuration
const i18next = require('i18next');
const Backend = require('i18next-fs-backend');
const middleware = require('i18next-http-middleware');
i18next.use(Backend)
.use(middleware.LanguageDetector)
.init({
debug: true, // debug option shows that "zh-hant" is loaded correctly when the Chinese site is accessed.
detection: detection_options,
fallbackLng: ['en', 'zh'],
backend: {
loadPath(lng, ns) {
if (lng === 'zh' || lng === 'zh-HK' || lng === 'zh-TW') {
return path.join(__dirname, 'locales/zh-hant.json');
} else if (lng === 'en-US') {
return path.join(__dirname, 'locales/en.json');
}
return path.join(__dirname, 'locales/{{lng}}.json');
}
}
})
app.use(middleware.handle(i18next));
const localeController = require('../controllers/locale');
app.get('/locale', localeController.getLocale);
// locale.js
exports.getLocale = async (req, res, next) => {
var i18next = require('i18next');
res.status(200).json(
i18next.t('tree', { returnObjects: true })
)
}

Use the t function from within the request object, like:
https://github.com/i18next/i18next-http-middleware/issues/51#issuecomment-1094851968

Related

Unable to read values from .env.local file in latest Cypress version

I am using latest Cypress version 10.11.0 and I am trying to read username, password from .env.local file in my system using process.env. But some how it not getting the values from the .env.local file ? Could someone please advise ?
Note : As a normal way, I am able to get values if I provide values in cypress.config.js (see below). Since username, password can't expose in config, i need to read it from .env.local file.
env:{
MYBOOKS_USERNAME:"testuser1#test.com",
MYBOOKS_PASSWORD:"Some_password"
},
// cypress.config.js file
const { defineConfig } = require("cypress");
module.exports = defineConfig({
chromeWebSecurity: false,
viewportWidth :1920,
viewportHeight:1080,
responseTimeout: 60000,
fixturesFolder: 'cypress/fixtures',
screenshotsFolder: 'cypress/screenshots',
videosFolder: 'cypress/videos',
numTestsKeptInMemory: 10,
video:false,
e2e: {
setupNodeEvents(on, config) {
require("cypress-grep/src/plugin")(config),
// implement node event listeners here
on('before:browser:launch', (browser, launchOptions ) => {
console.log("Print browser name: "+browser.name);
if (browser.name === 'chrome' || browser.name === 'chrome' && browser.isHeadless) {
launchOptions.args.push('--disable-features=SameSiteByDefaultCookies')
return launchOptions
}
});
on('task', {
logAtConsole (message) {
console.log('Printing the data using task::', message);
return null
}
});
},
baseUrl: "https://somebookssite-test.com",
specPattern: "cypress/e2e/booksSite/**/*.spec.{js,jsx,ts,tsx}",
},
});
// .env.local file
CYPRESS_MYBOOKS_USERNAME=testuser1#test.com,
CYPRESS_MYBOOKS_PASSWORD=Some_password
// commands.js file
const userName = process.env.MYBOOKS_USERNAME;
const userPassword = process.env.MYBOOKS_PASSWORD;
Cypress.Commands.add('loginRequest', (url) => {
helperFunctions.dynamicEnvironmentSelection();
const apiEndpoint = helperFunctions.getItemFromLocalStorage('apiUrl');
cy.request({
method: 'POST',
url: apiEndpoint + url,
contentType: 'application/json',
body: {
username: userName,
password: userPassword,
}
}).then((resp) => {
helperFunctions.setLoginCookies(resp);
});
});
You could use dotenv to read the .env.local file.
Using the convention for this tool, use key=value format in .env.local.
MYBOOKS_USERNAME="testuser1#test.com"
MYBOOKS_PASSWORD="Some_password"
const { defineConfig } = require('cypress')
const path = require('path')
const dotenv = require('dotenv')
const envLocal = dotenv.config({path: path.resolve(process.cwd(), '.env.local')})
module.exports = defineConfig({
env: {
// non-sensitive env vars hard-coded here
login_url: '/login',
products_url: '/products'
// sensitive env vars read in above from external file
...envLocal
},
Without dotenv
If you change the external file to .env.local.json, then it's possible to read it directly.
{
"MYBOOKS_USERNAME": "testuser1#test.com",
"MYBOOKS_PASSWORD": "Some_password"
}
const { defineConfig } = require('cypress')
const envLocal = require('./.env.local.json')
module.exports = defineConfig({
env: {
// non-sensitive env vars hard-coded here
login_url: '/login',
products_url: '/products'
// sensitive env vars read in above from external file
...envLocal
},

How can I serve data to front-end in Angular universal App from a server different from ExpressJS server provided by Angular universal?

I am having an issue with my Angular SSR App that is still in development stage. It's a routing issue. FYI, I am new in Angular (or web dev). The problem is, I can start SSR server and it will work fine if the route on the browser address bar is pointing to static page, that's, a page/component that doesn't fetch data from the backed; I can navigate to any route without any issues. But if I start the SSR server when the link on the browser address bar is pointing to a route that fetches data from the backend, it won't work. The same thing happens when I reload. The browser goes on loading without displaying anything and the terminal shows the error message below like it's looping until the server crashes.
ERROR HttpErrorResponse {
headers: HttpHeaders {
normalizedNames: Map {},
lazyUpdate: null,
headers: Map {}
},
status: 0,
statusText: 'Unknown Error',
url: 'http://localhost:4000/http://localhost:3000/api/posts?pagesize=2&page=1
,
ok: false,
name: 'HttpErrorResponse',
message: 'Http failure response for http://localhost:4000/http://localhost:30
0/api/posts?pagesize=2&page=1: 0 Unknown Error',
error: ProgressEvent {
type: 'error',
target: XMLHttpRequest {
onloadstart: null,
onprogress: null,
onabort: null,
onerror: null,
onload: null,
ontimeout: null,
onloadend: null,
_listeners: [Object],
onreadystatechange: null,
_anonymous: undefined,
readyState: 4,
response: null,
responseText: '',
responseType: 'text',
responseURL: '',
status: 0,
statusText: '',
timeout: 0,
upload: [XMLHttpRequestUpload],
_method: 'GET',
_url: [Url],
_sync: false,
_headers: [Object],
_loweredHeaders: [Object],
_mimeOverride: null,
_request: null,
_response: null,
_responseParts: null,
_responseHeaders: null,
_aborting: null,
_error: null,
_loadedBytes: 0,
_totalBytes: 0,
_lengthComputable: false
},
......
.....
....
....
lengthComputable: false,
loaded: 0,
total: 0
}
}
<--- Last few GCs --->
[4452:0000002EB0A8B870] 320685 ms: Mark-sweep 1982.1 (1990.6) -> 1981.2 (1990
6) MB, 1846.0 / 0.2 ms (average mu = 0.093, current mu = 0.015) allocation fai
ure scavenge might not succeed
[4452:0000002EB0A8B870] 322582 ms: Mark-sweep 1982.1 (1990.6) -> 1981.3 (1990
6) MB, 1785.5 / 0.3 ms (average mu = 0.077, current mu = 0.059) allocation fai
ure scavenge might not succeed
<--- JS stacktrace --->
==== JS stack trace =========================================
0: ExitFrame [pc: 00007FF7AB40D31D]
Security context: 0x004c643008d1 <JSObject>
1: createRenderer [00000150847C9A91] [C:\****\****\Desktop\MEAN STACK\Ng-1
\blog\dist\blog\server\main.js:~1] [pc=000003A6BF0102C6](this=0x02a68959f539 <p
atform_server_ServerRendererFactory2 map = 0000036DB8EC5ED9>,0x012cea722c29 <HT
LButtonElement map = 00000383BFA19079>,0x0298c6e98e51 <Object map = 0000019ACF0
1339>)
2: addComponentLogic(aka ad...
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - Java
cript heap out of memory
1: 00007FF7AA7D286F napi_wrap+119263
2: 00007FF7AA779536 v8::internal::OrderedHashTable<v8::internal::OrderedHashSe
,1>::NextTableOffset+38102
3: 00007FF7AA77A336 node::OnFatalError+438
4: 00007FF7AAFB7B3E v8::Isolate::ReportExternalAllocationLimitReached+94
5: 00007FF7AAF9FCF1 v8::SharedArrayBuffer::Externalize+833
6: 00007FF7AAE514CC v8::internal::Heap::EphemeronKeyWriteBarrierFromCode+1436
7: 00007FF7AAE5C710 v8::internal::Heap::ProtectUnprotectedMemoryChunks+1312
8: 00007FF7AAE59224 v8::internal::Heap::PageFlagsAreConsistent+3204
9: 00007FF7AAE4EA23 v8::internal::Heap::CollectGarbage+1283
10: 00007FF7AAE4D094 v8::internal::Heap::AddRetainedMap+2500
11: 00007FF7AAE6E3DD v8::internal::Factory::NewFillerObject+61
12: 00007FF7AABD19D1 v8::internal::interpreter::JumpTableTargetOffsets::iterato
::operator=+1665
13: 00007FF7AB40D31D v8::internal::SetupIsolateDelegate::SetupHeap+546925
14: 000003A6BF0102C6
npm ERR! code ELIFECYCLE
npm ERR! errno 134
npm ERR! blog#0.0.0 serve:ssr: `node dist/blog/server/main.js`
npm ERR! Exit status 134
npm ERR!
npm ERR! Failed at the blog#0.0.0 serve:ssr script.
npm ERR! This is probably not a problem with npm. There is likely additional lo
ging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Tony\AppData\Roaming\npm-cache\_logs\2021-08-24T16_40_37_
55Z-debug.log
How can I solve this issue? This is my Angular SSR below:
import 'zone.js/dist/zone-node';
const domino = require('domino');
// const proxyApp = require('./Backend/app');
import { ngExpressEngine } from '#nguniversal/express-engine';
import express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync } from 'fs';
// import { createProxyMiddleware } from 'http-proxy-middleware';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/blog/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Shim for the global window and document objects.
const windowObj = domino.createWindow(indexHtml);
global.window = windowObj;
global.document = windowObj.document;
global.self = windowObj;
global.IDBIndex = windowObj.IDBIndex;
global.navigator = windowObj.navigator;
global.getComputedStyle = windowObj.getComputedStyle;
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
server.get('/api/**', (req, res) => {
res.status(404).send('data requests are not yet supported');
});
// re-route requests to /api/ to REST api
// server.use('/api/**', createProxyMiddleware({ target: 'http://localhost:8000', changeOrigin: true }));
// server.use(proxyApp);
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// console.log(appRoutes);
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
And this is the excerpt from my NodeJs/ExpressJs server below:
const express = require('express');
const path = require('path');
const cors = require('cors');
const mongoose = require('mongoose');
const config = require('./config');
const app = express();
const { db: { URL } } = config;
// const Post = require('./models/post');
const postsRouter = require('./routes/posts');
const userAuthRouter = require('./routes/user');
const adminAuthRouter = require('./routes/admin');
// const commentsRouter = require('./routes/comments');
const topicRouter = require('./routes/topic');
// MongoDB database settings
// Grab mongoDB Authentication password from env. var with obj destructuring and pass it on to mongoose.
const connectionOptions = { useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true, /*useFindAndModify: false*/ };
// console.log(URL);
mongoose.connect(URL, connectionOptions )
.then(() => {
console.log('Connected Database');
})
.catch((err) => {
console.log('Connection Failed: ' + err);
});
const whitelist = ['http://localhost:4200', 'http://localhost:4000'];
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
app.use(express.urlencoded({extended: true})); // Parse URL-encoded bodies
app.use(express.json());
// app.use(cors())
app.use(cors({ origin: corsOptions }));
app.use(userAuthRouter);
app.use(postsRouter);
app.use(adminAuthRouter);
// app.use(commentsRouter);
app.use(topicRouter);
app.use('/images', express.static(path.join('Backend/images')));
module.exports = app;
I am adding additional info. The postsRouter and api/posts and the route displayed when ng serve is used are shown below:
// Get all Posts handler
exports.getPosts = (req, res, next)=> {
async.parallel({
posts: function(callback) {
const pageSize = +req.query.pagesize;
const currentPage = +req.query.page;
const topic = req.query.topic;
// Get All Posts
if (pageSize && currentPage && topic) {
Post.find({'topic': topic})
.skip(pageSize * (currentPage - 1))
.limit(pageSize)
.sort({publishDate: 'desc'})
.exec(callback);
}
// Group Post Based On Topic
if (pageSize && currentPage && !topic) {
// console.log(topic + " : " + id + ' 2nd');
Post.find({}, ' -topicID')
.skip(pageSize * (currentPage - 1))
.limit(pageSize)
.sort({publishDate: 'desc'})
.exec(callback);
}
},
postCount: function(callback) {
Post.countDocuments(callback);
}
},
function(err, results) {
if (err) {
return res.status(500).json({message: 'ERROR: Fetching Posts Failed!'});
}
res.status(200).json({
posts: results.posts,
postCount: results.postCount,
});
});
};
// Post service: Angular
getPosts(groupedPostTopic = '', postsPerPage?: number, currentPage?: number): void {
// Backend Params for pagination
const placeHolder = groupedPostTopic ? `/posts?topic=${groupedPostTopic}&` : '/posts?';
const queryParam = `pagesize=${postsPerPage}&page=${currentPage}`;
// Call to the server
const posts$ = this.http.get<{posts: Post[], postCount: number}>(BACKEND_URL + placeHolder + queryParam)
.pipe(map((postData) => {
return { posts: postData.posts.map((post: any) => {
const textStyle = this.dateTextStyle(post.publishDate);
return {
id: post._id,
topic: post.topic,
title: post.title,
postIntro: post.postIntro,
content: post.content,
imagePath: post.imagePath,
dateTextStyle: textStyle,
publishDate: post.publishDate,
creator: post.creator,
comments: post.comments
};
}),
postCount: postData.postCount
};
}));
// Activate spinner service until data arrival
this.isLoadingService.showContentUntilCompleted(posts$)
.subscribe(transformedPosts => {
this.posts = transformedPosts.posts;
this.postCount = transformedPosts.postCount;
// Data stored to be Observed using BehaviorSubject and/or asObservable() from rsjx
this.subject.next({posts: [...this.posts], postCount: transformedPosts.postCount});
});
}
// Displayed route when 'ng serve' is used
http://localhost:4200/posts
***OR***
http://localhost:4200/posts/:pageNo // For pagination
Thanks in advance!

Localized routes in koa

I'm developing a site with multiple languages. Some routes will therefore also have to be localized and I'm not sure how to do this properly.
I'm using #koa/router for routing.
For this example it's only English and Swedish but the site will handle more languages.
I can setup routes to match words in different languages like
router.get('/(create-account|skapa-konto)/', (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
});
But, I want the English site to only respond to '/sign-up' and send 404 for '/skapa-konto' (and vice versa).
In the real world the route would point to some controller function. So if I set up individual routes for each language I would have to change all localized routes manually should the controller function change in the future. That's something I would like to avoid ;)
Any suggestions?
I ended up solving this by extending the Router like this:
const LocalizedRouter = class extends Router {
/**
* Set up route mapping
* #param {object} options
*/
constructor(options) {
if (!Array.isArray(options.languages)) {
throw new TypeError('Languages must be of type Array');
}
super(options);
this.languages = options.languages;
}
/**
* Router function for GET method
* #param {string | Object<string, string>} RouteCollection
*/
get(routes, func) {
if (typeof(routes) === 'string') {
super.get(routes, func);
return;
}
if (typeof(routes) === 'object') {
for(const key in routes) {
if(!this.languages.includes(key)) {
continue;
}
if(typeof(func) !== 'function') {
throw new TypeError('Middleware must be a function');
}
const checkLanguageAndMount = async (ctx, next) => {
if(ctx.state.lang !== key) {
return next();
}
return func(ctx, next);
};
super.get(routes[key], checkLanguageAndMount);
}
return;
}
throw new TypeError('"Routes" must be a string or an object');
}
};
I can then set up my routes like this:
const myRouter = new LocalizedRouter({
languages: ['en', 'sv']
});
myRouter.get({
'en': '/create-account',
'sv': '/skapa-konto'
}, (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
};
This can probably be cleaned up but it does solve what I wanted to do.
EDIT: Fixed bug that caused 404 if two languages had identical paths
This problem interested me so I created a small github repo with some code. I'll try to explain here:
I created an array with some options:
const localeConfig = [
{
locale: "en",
routes: [
{
path: "/sign-up",
controllers: [enController],
method: "GET",
},
],
prefix: false,
},
{
locale: "se",
routes: [
{
path: "/skapa-konto",
controllers: [seController],
method: "GET",
},
],
prefix: false,
},
];
I then pass this object to a setupRoutes function that basically iterates the array, generating all the routes according to those options.
const setupRoutes = (localeConfig) => {
// Have some check to prevent duplicate routes
localeConfig.forEach((opt) => {
// Adding prefix according to option
const localePrefix = opt.prefix ? `/${opt.locale}` : "";
opt.routes.forEach((route) => {
const path = `${localePrefix}${route.path}`;
router[route.method.toLowerCase()].apply(router, [
path,
...route.controllers,
]);
});
});
};
So, for instance, if you were to change any of the controllers in either language you would only need to update the specific locale object.route.controllers. I imagine you could even have each different locale in a different file to have some modularity.
The github repo is here and I would really like to have you contribute to it if you have any idea on how to improve this.
Cheers!

Query parameters in express 4 give me a "No default engine was specified" error

I am developing a web app using the MEAN stack (no Mongo for now)
I am trying to pass the name of a file on the server using a query paramerer, the error happens when i get :
"localhost:8080/api/result?filename=for-debug-file-name"
It is working well if I remove the console.log() right below
But when I get the query parameter it gets me the "Error: No default engine was specified and no extension was provided”.
(This route correspond to api/result)
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
console.log(req.query('filename')); // ERROR
res.status(200).json({ "json-test": 42 });
})
module.exports = router;
Here are my angular routes :
const appRoutes: Routes = [
{
path: 'result',
component: ResultComponent,
},
{
path: 'upload',
component: UploaderComponent,
},
{
path: '',
redirectTo: '/upload',
pathMatch: 'full'
}];
And here is my ResultComponent.ts :
ngOnInit() {
this.getParsedDocumentData('for-debug-file-name');
}
getParsedDocumentData(fileName: string): Observable<string[]> {
let params = new URLSearchParams();
params.append('filename', fileName);
let options = new RequestOptions({ params: params });
return this.http.get('http://localhost:8080/api/result/', options)
.map(res => res.json())
.catch(this.handleError);
}
private handleError (error: any) {
return Observable.throw(error);
}
I would really appreciate your help as I have been stuck for 4 hours.
Thanks.
query method in request object does not exists. Instead use query property to access filename parameter.
console.log(req.query.filename);
Reference

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