Change configuration folder structure in sails.js - javascript

I just want to have another folder structure in sails.js. For example, something like:
/config
/application
routes.js
policies.js
/db
connections.js
...
It seems like when lifting application sails look for each of that files (console.log to /config/application/policies.js works when application getting lifted) anyway it does not work.
I try to add paths.config value to .sailsrc but it changes nothing. I an using sails version 0.10.5 now. Is there a way to do it?

I have found the solution. Actually, everything is very simple.
sails requires files from config folder recursively and tries to recognize them as configuration files. My problem was that I had copied most files as is:
// config/customfolder/connections.js
module.exports.connections = {
...
}
and here is the problem to require it. To make it work I just need to export config object as default export.
// config/customfolder/connections.js
module.exports = {
...
}

Related

How to split code into several bundles with Vue CLI3

I have a Vue project using TypeScript and built by Vue-CLI3.
What I'm trying to achieve is to get Webpack to build separate bundles for my workers. I've read about Webpack Code Splitting and about configureWebpack in vue.config.js, but so far had no luck in putting them together.
The project setup is the standard vue create type. I have a ./src/main.ts as the main entry point and a bunch of TypeScript modules, I want as separate bundles with their own dependency trees (I'm fine with code duplication if it can't be avoided).
I'd like to get
./dist/js/all main stuff
./dist/js/workers/worker1.6e3ebec8.js
./dist/js/workers/worker2.712f2df5.js
./dist/js/workers/worker3.83041b4b.js
So I could do new Worker(worker1.6e3ebec8.js) in the main code.
I could launch workers from the main package by generating javascript code, putting it into a blob and instantiating from that, but it looks rather awkward. Besides, my worker code import other modules, so it doesn't seem to be an option anyway.
I'm quite new to all of this, so maybe I'm not even heading in the right direction.
What is the usual way of doing that on this stack?
You can use import(), it will return a Promise and will resolve your module.
As you are using Vue-CLI 3, webpack is ready and it should split your bundle automatically.
const moduleName = 'coolModuleName'
import (
/* webpackChunkName: "[moduleName]" */
`#/my/module/path/${moduleName}.js`
).then(moduleCode => {
// use your module
})
// load them in parallel
const getModuleDynamically(path, moduleName) => import(
/* webpackChunkName: "[moduleName]" */
`#/${path}/${moduleName}.js`
)
Promise.all([
getModuleDynamically(path, moduleName1),
getModuleDynamically(path, moduleName2),
getModuleDynamically(path, moduleName3)
])
Got there! #aquilesb's answer did help, although I've failed to get getModuleDynamically() from the answer working after plenty of experimenting.
Update: Looks like with this solution I'm not able to use imports of npm modules. I've tried experimenting with worker-loader but haven't got anywhere so far.
Here are a few takeaways:
Create a separate webpack config for packing workers. The target: 'webworker' must be there. Call it with webpack --config ./webpack.config.workers.js, as Vue wouldn't know about that.
Create a separate tsconfig.json for workers TypeScript. The lib setting for workers must be there: "lib": ["esnext","webworker","scripthost"] as well as the proper include:[...]/exclude:[...] settings.
You may need to tell Vue to use the main tsconfig.json that has it's own "lib":["esnext","dom","dom.iterable","scripthost"] and include/exclude. This is done in vue.config.js, you will probably need to create it. I use chainWebpack configuration option of Vue config.
Let Webpack know you have dynamic loading by making calls to import() with static (i.e. not variable) names. I haven't found a way to do so in config file, but it doesn't matter: you can't help hardcoding the names somewhere, how else Webpack would know it has to bundle and split the code?
Somehow get the name(s) of generated files, as you must have at least one of them at runtime to do new Worker(filename). I used the --json option of Webpack CLI for that.
There are many ways all of this can be achieved. This is what this ended up looking like in my project:
Folder structure:
webpack.config.workers.js
vue.config.js
tsconfig.base.json
src/main/
src/main/tsconfig.json -- extends tsconfig.base.json
src/shared/ -- this code may be duplicated by the Vue app bundles and by workers bundle
src/workers/
src/workers/tsconfig.json -- extends tsconfig.base.json
webpack.config.workers.js: contains a single entry – the main worker file, that loads the other stuff.
entry: {
worker: './src/workers/worker.ts'
}
build.workers.sh: the script calls Webpack CLI and produces a JSON file with the resulting workers names (trivial actions on folders are omitted). The only one I need is called "worker". The rest is to be dynamically loaded by it.
#!/bin/bash
# Map entry name -> bundle file name
# "assetsByChunkName":{"entryN":"entryN.[hash].js", ...}
json=$(webpack --config ./webpack.config.workers.js --json $#|tr -d "\n\r\t "|grep -Eo '"assetsByChunkName":.+?}')
# Remove "assetsByChunkName"
json=$(echo "${json:20}")
echo $json
echo $json > "$target/$folder/workers.json"
Load workers.json at runtime. The other option would be to use it at compile time by providing Vue config with const VUE_APP_MAIN_WORKER = require("path to/workers.json").worker and using this env constant.
Now that we have the name of the main worker file, we can do new Worker("main worker file path we've got from webpack").
The main worker file contains the function that statically references other modules and dynamically loads them. This way Webpack knows what to bundle and how to split the code.
enum WorkerName {
sodium = "sodium",
socket = "socket"
}
function importModule(name: WorkerName): Promise<any> {
switch (name) {
case WorkerName.sodium:
return import(
/* webpackChunkName: "sodium" */
"workers/sodium"
);
case WorkerName.socket:
return import(
/* webpackChunkName: "socket" */
"workers/socket"
);
}
}
Use the postMessage/message event API to tell your main worker code what to load.
const messageHandler = (e: MessageEvent) => {
// here goes app-specific implementation of events
// that gets you the moduleName in the end
importModule(moduleName).then((work) => {
// do smth
});
};
Now, to the correct answer.
To achieve the following:
Using webworkers
Using both dynamic imports and normal imports in webworker code
Sharing code between webworkers and main app
I had to add a separate rule for worker-loader in vue.config.js and also to add babel-loader. It took me some time to find the correct solution, but I dropped the previous one (in my other answer) in the end. I still use separate tsconfig.js for main and for webworkers.
What I'm still not happy with, is that vue-cli–or rather fork-ts-checker plugin–doesn't seem to know the webworker-specific types in my worker classes (so I can't use DedicatedWorkerScope, for instance).

Run intern functional tests against static web app on localhost

I have a static web app. Html, JS (requirejs modules), and some CSS.
Currently the 'serverUrl' is being set through a property module, which i can 'require' and use values from it:
define({
serverUrl: 'https://some.api/path/'
})
I have Intern setup to run functional tests in the browser using src/index.html as the entry point.
return this.remote
.get(require.toUrl('src/index.html'))
Given the serverUrl is hardcoded in the properties file, I'm trying to find a way to run tests against the web app where serverUrl is pointing to localhost:1234/someFakeServer so I can test error scenarios and such.
I've trawled the web but can't find anyone doing anything remotely similar to me, which makes me think I'm doing something obviously wrong. There's solutions for NODE apps, using config modules, but because I never 'start' my web app - its just files, these won't work for me.
Some solutions I've thought about, but can't figure out how to achieve:
Intern is proxying the files on :9000, so if I can somehow 'build' an application with another properties file pointing to localhost, all is good. But I've no idea how to do that - I've looked at webpack and similar but they don't seem to do what I want.
I've looked at Interns 'Setup' config item, which allows a function to be run before the tests are started - so I thought about modifying the properties file in there, but seems too hacky and not sure how I'd put it back...
Assuming the properties file is accessible to Intern, you could simply have Intern load the properties file and pull the server URL out of it. If you have multiple potential properties files, the one being used can be set in the Intern config or passed in as a custom command line variable (which would be used to set a property in the Intern config). The test module can get the name of the properties file from Intern's config and then load the relevant file. It could look something like this (untested):
// intern config
define({
// ...
propertiesFile: 'whatever',
})
// test file
define([ 'intern', ... ], function (intern, ...) {
registerSuite({
// ...
'a test': function () {
var did = this.async();
var remote = this.remote;
require([
intern.config.propertiesFile
], dfd.callback(function (props) {
return remote.get(props.url)
.otherStuff
});
}
});
});

Meteor ES6 modules relative paths

Just came back to meteor, and trying to figure out how to structure a project, I'm getting stuck with how to handle relative paths, my project structure is like this:
-root
--client
----components
-----layout
...... MainLayout.jsx
--server
--lib
.. routes.jsx
If I try to import in my routes.jsx file like this './../client/components/layout/MainLayout.jsx' I get the error 'Cannot find module './../client/components/layout/MainLayout.jsx...
However if I move MainLayout.jsx to the root folder, i can reach the component file like this without problems './../MainLayout.jsx'.
Maybe I'm missing something here? as I said just returned to JS programming.
When the router.js file in the root. You have use instead of
'./../client/components/layout/MainLayout.jsx'
this:
'../client/components/layout/MainLayout.jsx'
or just:
'../client/components/layout/MainLayout'

node.js / backbone.js application load configuration file settings

I have a node / backbone / sails project that needs to have deployment specific configuration files loaded.
So in the sails portion of the app, I can place a config file in
myapp/config/mysettings.js
and reference it sails.config.mysettings.foo. This works as expected.
But in the backbone portion of the app, I cannot for the life of me figure out how to reference that same file (... are snips for brevity).
define([
'jquery',
'async',
...,
**'/config/mysettings.js'**
], function ($, async, ..., **mysettings**) {
relevantAjaxFunction: function() {
...
**fail**:
console.log(mysettings.foo);
Produces an undefined message in the console. What's the correct way to reference an application wide settings file like this? I've looked and cannot find anything, which makes me think it's either super obvious or I'm phrasing the question wrong.
Sails doesn't make your config files publicly browsable. That would be...bad.
You have a few options here that I can think of:
Make a symlink inside your assets folder that points to the config file. The tricky part here is that the symlink will be copied as-is into your .tmp/public folder, so if you use a relative path in your symlink, it needs to be the relative path from .tmp/public to config/mysettings.js. Or you could just use an absolute path in the symlink.
Make a custom route that just streams the file using fs:
require('fs')
.createReadStream(sails.config.paths.config+"/mysettings.js")
.pipe(res);
although you should really use the path module for determining the file path, and wrap the whole thing in a try/catch just in case...
Use a policy to add the config settings in a local variable for every request, for example:
module.exports = function myConfigPolicy(req, res, next) {
res.locals.mysettings = sails.config.mysettings;
return next();
}
and in your views/layout.ejs do something like:
<script type="text/javascript">
var mySettings = <%= JSON.stringify(mysettings) %>;
</script>
Adjusting for your template engine of choice. Of course this doesn't use AMD to load the config at runtime, so if that's a concern go with choice #1 or #2!

How do I connect bower components with sails.js?

I'd like to be able to install Javascript dependencies through bower and use them in a sails.js app, but I can't figure out a way to do this with out just copying an pasting files from the bower_components folder to the Sails assets folder.
Ideally I think I'd like to use requirejs and point to the bower components in the main.js file. I may be trying to pound a square peg in a round hole, please let me know if so. Any thoughts on managing components/libraries with Sails are welcome.
In Sails 0.10 the 'assets/linker' directory no longer exists, however I found a lead on simple solution that gives some automation to the bower -> asset linker workflow while also allowing some granular control over what exactly ends up getting linked.
The solution is adding grunt-bower to your Sails.js compileAssets task
grunt.registerTask('compileAssets', [
'clean:dev',
'bower:dev',
'jst:dev',
'less:dev',
'copy:dev',
'coffee:dev'
]);
Then configure your grunt-bower task like so (as tasks/config/bower.js):
module.exports = function(grunt) {
grunt.config.set('bower', {
dev: {
dest: '.tmp/public',
js_dest: '.tmp/public/js',
css_dest: '.tmp/public/styles'
}
});
grunt.loadNpmTasks('grunt-bower');
};
This will automatically copy your bower js and css files to the proper place for Sail's asset linker to find and automatically add to your project's layout template. Any other js or css files will still automatically be added after your bower files.
However this is still no silver bullet as this setup has 2 big caveats to it:
1 - The files that are added through bower-grunt have to be listed in bower.json's main array. If you see a file isn't being loaded you would expect to be, you must either edit that packages bower.json file OR add the dependency manually through grunt-bower's packageSpecific options.
2 - The order of bower files in the asset linker is currently alphabetical. My only recourse to adjust this order so far is tinkering around with an additional grunt task to manually re-order files prior to the rest of Sail's compileAssets task. However this one I'm confident there is something grunt-bower could do by supporting package copy ordering.
Note: the following answer is no longer completely relevant to the current version of SailsJS because there is no support for the linker folder as of SailsJS 0.10.
See: Sails not generating linker
Original answer:
I was able to figure out a solution for this, which is actually pretty simple. I had not realized you could configure where bower places it's files.
Create a .bowerrc file and change the directory where bower components are installed, in the case of Sailjs they should be put into the assets folder.
/*
* Create a file called .bowerrc and put the following in it.
* This file should be in the root directory of the sails app.
*/
{
"directory": "assets/linker/bower_components"
}
Sails will then use grunt to copy them to the .tmp/public/assets folder whenever a file is change. If you don't wish to have sails continually deleting and then recopying those files you can exclude them in the grunt file.
/*
* This is not necessary, but if you have a lot of components and don't want
* them constantly being deleted and copied at every file change you can update
* your Gruntfile.js with the below.
*/
clean: {
dev: ['.tmp/public/**',
'!.tmp/public',
'!.tmp/public/bower_components/**'],
build: ['www']
},
One tip on using requirejs with sails. By default you will get an error from the socket.io file since sails will load it without using requirejs. This will throw an error since socket.io supports amd style loading, more details here http://requirejs.org/docs/errors.html#mismatch.
The simplest way to fix this is to just comment out the lines near the end of the socket.io.js.
/*
* Comment the below out in the file assets/js/socket.io.js, if using requirejs
* and you don't want to modify the default sails setup or socket.io.
*/
if (typeof define === "function" && define.amd) {
define([], function () { return io; });
}
The other way would be to recode the sails files in assets/js named "socket.io.js", "sails.io.js" and app.js to be amd modules.
The simplest way I've found of achieving this is simply to add the individual Bower components to your tasks/pipeline.js file. For example, here's how you might add Modernizr:
// Client-side javascript files to inject in order
// (uses Grunt-style wildcard/glob/splat expressions)
var jsFilesToInject = [
// Load sails.io before everything else
'js/dependencies/sails.io.js',
// Dependencies like jQuery, or Angular are brought in here
'js/dependencies/**/*.js',
// =========================================================
// Modernizr:
'bower_components/modernizr/modernizr.js',
// =========================================================
// All of the rest of your client-side js files
// will be injected here in no particular order.
'js/**/*.js'
];
A link to bower_components/modernizr/modernizr.js then gets inserted in the <!--SCRIPTS--> placeholder in your layout template, and it also gets minified, etc, when you run sails lift --prod.
Sorry for my late.
I think include bower_components in linker it's a bad idea, because when you will lift sails, everything in it will be copied in .tmp and include in tags.
For example, if you have include Angular with bower, you will have two inclusions in your script: one for angular.js and one for angular.min.js. And having both is a mistake.
Here is my solution on my projects :
I have created a folder bower_component in my root directory.
I have added this line in my Gruntfile.js in the array files in copy:dev
{ '.tmp/public/linker/js/angular.js': './bower_components/angular/angular.js' }
And for each files I want to include, I need to manually add a new line in this array. I haven't find an another automatic solution. If someone finds a better solution... ;)
There is more than one approach to hooking up SailsJS and Bower.
A package for SailsJS that integrates Bower via a custom generator exists here:
https://www.npmjs.com/package/sails-generate-bower
There is one for Gulp as well.

Categories

Resources