I am trying to pre-cache some of my static app shell files using service worker. I can't use 'sw-appcache' as I am not using any build system. However, I tried using 'sw-toolbox' but I am not being able to use it for pre-caching.
Here is what I am doing in my service worker JS:
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('gdvs-static').then(function(cache) {
var precache_urls = [
'/',
'/?webapp=true',
'/css/gv.min.css',
'/js/gv.min.js'
];
return cache.addAll(precache_urls);
});
);
});
I've also tried this:
importScripts('/path/to/sw-toolbox.js');
self.addEventListener('install', function(event) {
var precache_urls = [
'/',
'/?webapp=true',
'/css/gv.min.css',
'/js/gv.min.js'
];
toolbox.precache(precache_urls);
});
Here is the URL of my app: https://guidedverses-webapp-staging.herokuapp.com/
Here is the URL of my service worker file: https://guidedverses-webapp-staging.herokuapp.com/gdvs-sw.js
What am I missing?
Silly me. I forgot to add the fetch handler into my service worker. I thought it works like appchache and automatically returns the cached data when matches with the cache URL. I underestimated the power of Service Worker. Following code did the trick for me.
this.addEventListener('fetch', function(event) {
console.log(event.request.url);
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
Related
I am using glue to spin up the hapi server so I gave the json object with connection and registration details.
I have 10 routes and i need to use authentication strategy for all the 10 routes, So followed the below steps
1) I have registered the xyz custom auth plugin
2) Defined the strategy server.auth.strategy('xyz', 'xyz', { });
3) At every route level I am enabling auth strategy
auth: {
strategies: ['xyz'],
}
How can I give below line to glue configuration object itself.
server.auth.strategy('xyz', 'xyz', { });
Glue.compose(ServerConfig, { relativeTo: baseDir }, (err, server) => {
internals.server = server;
})
One more question here is, in this line server.auth.strategy('xyz', 'xyz', { from json file}); I am reading the JSON data from a config file. When I change the data in this JSON file I dont want to restart the server manually to load the modified data. Is there any plugin or custom code to achieve this?
I figured out a general workaround for when you want to do setup that Glue does not directly support (AFAIK) and you also don't want to keep adding to index.js.
Create a plugins folder where your manifest.js is located.
Create a file plugins/auth.js (in this case). Here you will have a register callback with access to the server object and you can make setup calls that go beyond what Glue does declaratively.
Add a plugin item to manifest.js pointing to your plugin file.
in manifest.js:
register: {
plugins: [
{
plugin: './plugins/auth',
},
]
}
in plugins/auth.js:
module.exports = {
name: 'auth',
async register (server) {
await server.register([
require('#hapi/cookie'),
]);
server.auth.strategy('session', 'cookie', {
cookie: {
name: 'sid-example',
password: '!wsYhFA*C2U6nz=Bu^%A#^F#SF3&kSR6',
isSecure: false
},
redirectTo: '/login',
validateFunc: async (request, session) => {
const account = await users.find(
(user) => (user.id === session.id)
);
if (!account) {
return { valid: false };
}
return { valid: true, credentials: account };
}
});
server.auth.default('session');
},
};
(auth setup code is from the Hapi docs at enter link description here)
This is the way I have found where I can call things like server.auth.strategy() sort-of from manifest.js.
Note: Auth is not a great example for this technique as there is a special folder for auth strategies in lib/auth/strategies.
I am trying to super-optimize a PWA by combining and minifying as much as I can. My application is based mostly on a google tutorial on service workers - and as such I have code such as this in my service worker:
var filesToCache = [
'/',
'/index.html',
'/scripts/app.js',
'/styles/inline.css'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
I have a gulpfile.js which, among other things uses gulp-smoosher to inline my css during build:
<!-- smoosh -->
<link rel="stylesheet" type="text/css" href="styles/inline.css">
<!-- endsmoosh -->
Which works great - it inlines my css directly into the HTML - however clearly my filesToCache in my serviceworker now has an entry which wont exist in the build
var filesToCache = [
'/',
'/index.html',
'/scripts/app.js',
'/styles/inline.css' // <!--- this shouldn't be here in the build
];
Is there any options, using a gulp task or otherwise (perhaps some sort of config for this which can be updated on build) to resolve this issue?
I ended up solving this by making the following changes.
Move the filesToCache variable to its own json file - filesToCache.json
Update my service worker to load that file in during install
Use gulp-json-editor to manipulate the file on build.
Code in gulpfile
const jsonEditor = require('gulp-json-editor');
// snip
gulp.task("filesToCache", function(){
var out = folder.build;
return gulp.src(folder.src + "filesToCache.json")
.pipe(jsonEditor(function(json){
json.splice(json.indexOf("/styles/inline.css"),1);
return json;
}))
.pipe(gulp.dest(out));
});
Code in service worker
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
return fetch("/filesToCache.json").then(function(response){
if(response && response.ok){
return response.json()
}
throw new Error("Failed to load files to cache for app shell")
})
.then(function(filesToCache){
console.log('[ServiceWorker] Caching app shell', filesToCache);
return cache.addAll(filesToCache);
})
.catch(function(error){
console.error(error)
})
})
);
});
Hope this helps someone in future!
I'm trying to set the current Git SHA in my project's Grunt configuration, but when I try to access it from another task it isn't available, What am I missing?
grunt.registerTask('sha', function () {
var done = this.async();
grunt.util.spawn({
cmd: 'git',
args: ['rev-parse', '--short', 'HEAD']
}, function (err, res) {
if (err) {
grunt.fail.fatal(err);
} else {
grunt.config.set('git', {sha: res.stdout});
if (grunt.option('debug') || grunt.option('verbose')) {
console.log("[sha]:", res.stdout);
}
}
done();
});
});
After running the task, I expect the config to be available in another task configuration:
requirejs: {
dist: {
...
out: '<%= app.dist %>/scripts/module_name.<%= git.sha %>.js'
...
}
}
So... What's the problem?
The problem is that Require JS is writing to the file public/scripts/module_name..js, the SHA is not available in the configuration (when the filename should be public/scripts/module_name.d34dc0d3.js).
UPDATE:
The problem is that I'm running requirejs tasks with grunt-concurrent, so the Grunt configuration is not available for requirejs.
grunt.registerTask('build', [
...
'getsha',
'concurrent:dist',
...
]);
And the concurrent task, looks like:
concurrent: {
dist: [
...
'requirejs',
...
]
}
Since grunt-concurrent will spawn tasks in child processes, they do not have access to the context of the parent process. Which is why doing grunt.config.set() within the parent context is not available in the config of the child context.
Some of the solutions to make the change available in the child context are:
Write the data to the file system
Write the data to a temporary file with grunt.file.write('./tmp/gitsha', res.stdout) and then have the task being ran in a child process read the temporary file:
out: (function() {
var out = grunt.config('app.dist') + '/scripts/module_name.';
if (grunt.file.exists('./tmp/gitsha')) {
out += grunt.file.read('./tmp/gitsha');
} else {
out += 'unknown';
}
return out + '.js';
}())
Use a socket
This is a very convoluted solution but a solution nonetheless. See the net node docs: http://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener on creating a server on the parent process then have the child process connect to the socket for the data.
Or check out https://github.com/shama/blackbox for a library that makes this method a bit simpler.
Fork the parent process instead of spawn/exec
Another method is to use fork: http://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options instead of grunt-concurrent. Fork lets you send messages to child processes with child.send('gitsha') and receive them in the child with process.on('message', function(gitsha) {})
This method also can get very convoluted.
Use a proxy task
Have your sha task set the config as you're currently doing:
grunt.registerTask('sha', function() {
grunt.config.set('git', { sha: '1234' });
});
Change your concurrent config to call a proxy task with the sha:
grunt.initConfig({
concurrent: {
dist: [
'proxy:requirejs:<%= git.sha %>'
]
}
});
Then create a proxy task that runs a task with setting the passed value first:
grunt.registerTask('proxy', function(task, gitsha) {
grunt.config.set('git', { sha: gitsha });
grunt.task.run(task);
});
The above can be simplified to set values specifically on requirejs but just shown here as a generic example that can be applied with any task.
https://github.com/davebryson/meteor_file_upload_example
the above is the original code using Meteor.router
and i wanted to convert this code only using iron-router, instead of previous router package
but the problem is when i upload file,
i can't understand how to convert these code using iron-router api.
i think there's a problem with index.html and serverside main.js code but i can't fix that.
would you please convert this code below using iron router plz?
in serverside main.js in the original code.
Meteor.Router.configure(
{
bodyParser:
{
uploadDir: 'uploads',
hash: 'sha1',
keepExtensions : true
}
}
);
Meteor.Router.add('/upload/file', 'POST',
function()
{
var title = this.request.body.fileTitle,
size = this.request.files.fileInfo.size,
path = this.request.files.fileInfo.path,
name = this.request.files.fileInfo.name,
obj = {title: title, size: size, path: path, name: name};
Files.insert(obj);
// redirect to root
return [301, {Location: '/'}, ""]
}
);
and i'v already converted the code in clientside main.js like below
Router.map(function () {
this.route('listFiles', {path: '/'});
this.route('addFile', {path: '/add'});
});
Template.listFiles.helpers({
list: function () {
return Files.find({});
}
});
Template.listFiles.events({
'click #addFileBtn' : function(e) {
e.preventDefault();
Router.go('/add');
}
});
Template.addFile.events({
'click #cancelBtn': function(e){
e.preventDefault();
Router.go('/');
}
});
Meteor.subscribe("files");
the point is that i can't handle how to use http method in serverside code with iron-router
Meteor now directly allows you to attach middleware to the server using the WebApp package, with standard Connect handlers.
Put webapp inside .meteor/packages of your app. You can then attach middleware to WebApp.connectHandlers inside your app. It's not too well documented yet, but treat it as a normal connect server.
I have a web application that uses requirejs to load its modules. The web applications works without problems in any desktop browser, it also works on iOS and Android when packaged with Cordova. Does however NOT work when building a Windows Phone 8 Cordova application.
I get the following error:
"View Not Found: Searched for "views/shell" via path "text!views/shell.html"
(I'm using Durandal)
I have the following application structure:
lib/require/require.js
www/app/viewmodels/shell.js
www/app/views/shell.html
www/app/main.js
www/index.html (contains line: )
:
www/app/main.js contains the following code:
requirejs.config({
//baseUrl: 'x-wmapp0:www/app',
baseUrl: 'app',
enforceDefine: true,
paths: {
'text': '../lib/require/text',
'durandal':'../lib/durandal/js',
'plugins' : '../lib/durandal/js/plugins',
'transitions' : '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-2.3.0',
'bootstrap': '../lib/bootstrap3/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1',
'modules' : 'modules'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
define(['durandal/system', 'durandal/app', 'durandal/viewLocator', 'bootstrap'], function (system, app, viewLocator, bootstrap) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'MyApp';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function() {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention('viewmodels', 'views', 'views');
//Show the app by setting the root view model for our application with a transition.
app.setRoot('viewmodels/shell', 'entrance', 'applicationHost');
});
});
This code works perfectly on all devices except WP8. I tried the line baseUrl: 'x-wmapp0:www/app' to set the actual path used internally by WP8 Cordova, but that does not work. Is that because it is not a path starting with '/' or http?
Any ideas on how to configure requirejs to be able to load modules and views using requirejs?
See if this is any help to you http://mikaelkoskinen.net/durandal-phonegap-windows-phone-8-tutorial/
Also, as of the 18th October 2013, Phonegap Build has added beta support for WP8. Add "winphone: as the gap:platform and ensure you're using phonegap 3.0. We're currently having no luck but maybe you can use that.
I just solve this problem for myself as well, what I ended up doing was waiting for the deviceloaded event before I loaded my main.js.
Cordova applies a patch to XMLHttpRequest which has been applied when deviceloaded has been triggered.
In my app I it ended up looking similar to this:
<script src="js/require.js"></script>
<script>
document.addEventListener('deviceloaded', function() {
var script = document.createElement('script');
script.src = 'js/main.js';
document.body.appendChild(script);
}, false);
</script>
I hope that works out for you as well!
One other thing, I noticed in my app that if I try to load weinre I couldn't get my app started at all with require.js. I haven't been able to research this further yet though.
I recommend to set a breakpoint to cordovalib/XHRHelper.cs HandleCommand method and see what is going on. Also the following version of HandleCommand could work better for you
public bool HandleCommand(string commandStr)
{
if (commandStr.IndexOf("XHRLOCAL") == 0)
{
string url = commandStr.Replace("XHRLOCAL/", "");
Uri uri = new Uri(url, UriKind.RelativeOrAbsolute);
try
{
using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoFile.FileExists(uri.AbsolutePath))
{
using (
TextReader reader =
new StreamReader(isoFile.OpenFile(uri.AbsolutePath, FileMode.Open, FileAccess.Read))
)
{
string text = reader.ReadToEnd();
Browser.InvokeScript("__onXHRLocalCallback", new string[] { "200", text });
return true;
}
}
}
url = uri.AbsolutePath;
}
catch { }
var resource = Application.GetResourceStream(new Uri(url, UriKind.Relative)) ??
// for relative paths and app resources we need to add www folder manually
// there are many related issues on stackoverflow + some prev worked sample don't because of this
Application.GetResourceStream(new Uri("www/" + url, UriKind.Relative));
if (resource == null)
{
// 404 ?
Browser.InvokeScript("__onXHRLocalCallback", new string[] { "404" });
return true;
}
else
{
using (StreamReader streamReader = new StreamReader(resource.Stream))
{
string text = streamReader.ReadToEnd();
Browser.InvokeScript("__onXHRLocalCallback", new string[] { "200", text });
return true;
}
}
}
return false;
}