Was going through gulp packages on the npm website and came across this package called gulp-rename-md5. Is there a scenario where renaming a file using MD5 is useful and why?
I've used a similar tool for cache busting (called gulp-freeze which adds an MD5 hash of the file contents to the filename).
When you update a CSS or JS file you want users to get the latest version of that file when they visit your site. If your file is named "app.min.js" and you update it, their browsers might not pull down the latest file. If you're using a CDN even clearing the browser cache probably won't request the new file.
I've used gulp-freeze with gulp-filenames (to store the name of the cache busted file) and gulp-html-replace (to actually update the <link /> or <script /> tags with the name of this cache busted file in the html). It's really handy.
CODE SAMPLE
This will get your files, use gulp-freeze to build the hash, use gulp-filenames to store the name of that file, then write that to the html with gulp-html-replace. This is tested and working
var gulp = require("gulp"),
runSequence = require("run-sequence"),
$ = require("gulp-load-plugins")();
gulp.task("build", () => {
runSequence("js", "write-to-view");
});
gulp.task("js", () => {
return gulp
.src("./js/**/*.js")
.pipe($.freeze())
.pipe($.filenames("my-javascript"))
.pipe(gulp.dest("./"));
});
gulp.task("write-to-view", () => {
return gulp
.src("index.html")
.pipe(
$.htmlReplace(
{
js: $.filenames.get("my-javascript")
},
{ keepBlockTags: true }
)
)
.pipe(gulp.dest("./"));
});
EDIT
I should add that index.html just needs these comments so gulp-html-replace knows where to write the <script /> element
<!--build:js-->
<!-- endbuild -->
One of advantages is that you can setup your app to cache files with MD5 sum in their name (e.g. mystyle.a345fe.css) for long time (several months) because you know that this file will never be modified. This will save you some traffic and your web will be faster for returning visitors.
Related
I'm getting complaints about 02packages.details.txt.gz when I'm trying to install a package with cpan. The error is "Warning: Your /root/.cpan/sources/modules/02packages.details.txt.gz does not contain a Line-Count header.
"
Apparently, according to cpan, this is not a valid zip file, which it isn't. It's just a web page which I'll paste later. I do not use a proxy and I had five mirrors in my configuration. I've deleted one then the next off the list and I'm still getting the same data back. I have deleted the file and attempted to allow cpan to fetch it again. I have fetched the page with curl and I'm seeing a web page and not anything that looks like a gz file.
I have tried "install cpan" from the cpan command line in case I missed an update but that runs into the exact same problem.
Example fetch:
curl http://noodle.portalus.net/CPAN/modules/02packages.details.txt.gz
Result (parts obsfucated)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
function getCookie(c_name) { // Local function for getting a cookie value
if (document.cookie.length > 0) {
c_start = document.cookie.indexOf(c_name + "=");
if (c_start!=-1) {
c_start=c_start + c_name.length + 1;
c_end=document.cookie.indexOf(";", c_start);
if (c_end==-1)
c_end = document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
}
return "";
}
function setCookie(c_name, value, expiredays) { // Local function for setting a value of a cookie
var exdate = new Date();
exdate.setDate(exdate.getDate()+expiredays);
document.cookie = c_name + "=" + escape(value) + ((expiredays==null) ? "" : ";expires=" + exdate.toGMTString()) + ";path=/";
}
function getHostUri() {
var loc = document.location;
return loc.toString();
}
setCookie('XXXXXXXXXXXXXXXXXXXXiw_9289NNNNNNNNJAX666', 'MY.IP.ADDRESS.HERE', 10);
try {
location.reload(true);
} catch (err1) {
try {
location.reload();
} catch (err2) {
location.href = getHostUri();
}
}
</script>
</head>
<body>
<noscript>This site requires JavaScript and Cookies to be enabled. Please change your browser settings or upgrade your browser.</noscript>
</body>
</html>
Note that this is the same as the contents as fetched by cpan.
CPAN stands for Comprehensive Perl Archive Network. The network once consisted of a slew of mirrors, but this has been replaced with a transparent Content Delivery Network (CDN).
The once-valuable service offered by the mirrors is therefore no longer needed, so many sites have ceased mirroring CPAN. It appears to be the case for the mirror your cpan is configured to use.
A simple configuration change is needed to fix the issue. From within the cpan tool, use either
o conf init urllist
or
o conf urllist http://www.cpan.org/
The first picks a mirror from the "official" list. There is only one URL in the list: http://www.cpan.org/. The second picks that URL directly.
To save the changes, follow up with
o conf commit
(This may not be necessary for everyone, but there's no harm in always using it.)
Example:
C:\Users\ikegami>cpan
Loading internal logger. Log::Log4perl recommended for better logging
Unable to get Terminal Size. The Win32 GetConsoleScreenBufferInfo call didn't work. The COLUMNS and LINES environment variables didn't work. at C:\progs\sp5032001001\perl\vendor\lib/Term/ReadLine/readline.pm line 410.
cpan shell -- CPAN exploration and modules installation (v2.28)
Enter 'h' for help.
cpan> o conf init urllist
Now you need to choose your CPAN mirror sites. You can let me
pick mirrors for you, you can select them from a list or you
can enter them by hand.
Would you like me to automatically choose some CPAN mirror
sites for you? (This means connecting to the Internet) [yes] y
Trying to fetch a mirror list from the Internet
Fetching with LWP:
init/MIRRORED.BY
Fetching with LWP:
init/MIRRORED.BY.gz
Fetching with LWP:
http://www.perl.org/CPAN/MIRRORED.BY
Looking for CPAN mirrors near you (please be patient)
.. done!
New urllist
http://www.cpan.org/
commit: wrote 'C:\progs\sp5032001001\perl\lib/CPAN/Config.pm'
cpan> quit
Lockfile removed.
C:\Users\ikegami>
commit: wrote indicates the changes were saved, so I didn't need to use o conf commit.
It seems like indeed, one after another of my mirrors was stale. Currently using mirrors.namecheap.com and all looking good.
I need to pull in the contents of a program source file for display in a page generated by Gatsby. I've got everything wired up to the point where I should be able to call
// my-fancy-template.tsx
import { readFileSync } from "fs";
// ...
const fileContents = readFileSync("./my/relative/file/path.cs");
However, on running either gatsby develop or gatsby build, I'm getting the following error
This dependency was not found:
⠀
* fs in ./src/templates/my-fancy-template.tsx
⠀
To install it, you can run: npm install --save fs
However, all the documentation would suggest that this module is native to Node unless it is being run on the browser. I'm not overly familiar with Node yet, but given that gatsby build also fails (this command does not even start a local server), I'd be a little surprised if this was the problem.
I even tried this from a new test site (gatsby new test) to the same effect.
I found this in the sidebar and gave that a shot, but it appears it just declared that fs was available; it didn't actually provide fs.
It then struck me that while Gatsby creates the pages at build-time, it may not render those pages until they're needed. This may be a faulty assessment, but it ultimately led to the solution I needed:
You'll need to add the file contents to a field on File (assuming you're using gatsby-source-filesystem) during exports.onCreateNode in gatsby-node.js. You can do this via the usual means:
if (node.internal.type === `File`) {
fs.readFile(node.absolutePath, undefined, (_err, buf) => {
createNodeField({ node, name: `contents`, value: buf.toString()});
});
}
You can then access this field in your query inside my-fancy-template.tsx:
{
allFile {
nodes {
fields { content }
}
}
}
From there, you're free to use fields.content inside each element of allFile.nodes. (This of course also applies to file query methods.)
Naturally, I'd be ecstatic if someone has a more elegant solution :-)
ES6 modules allows us to create a single point of entry like so:
// main.js
import foo from 'foo';
foo()
<script src="scripts/main.js" type="module"></script>
foo.js will be stored in the browser cache. This is desirable until I push a new version of foo.js to production.
It is common practice to add a query string param with a unique id to force the browser to fetch a new version of a js file (foo.js?cb=1234)
How can this be achieved using the es6 module pattern?
There is one solution for all of this that doesn't involve query string. let's say your module files are in /modules/. Use relative module resolution ./ or ../ when importing modules and then rewrite your paths in server side to include version number. Use something like /modules/x.x.x/ then rewrite path to /modules/. Now you can just have global version number for modules by including your first module with
<script type="module" src="/modules/1.1.2/foo.mjs"></script>
Or if you can't rewrite paths, then just put files into folder /modules/version/ during development and rename version folder to version number and update path in script tag when you publish.
HTTP headers to the rescue. Serve your files with an ETag that is the checksum of the file. S3 does that by default at example.
When you try to import the file again, the browser will request the file, this time attaching the ETag to a "if-none-match" header: the server will verify if the ETag matches the current file and send back either a 304 Not Modified, saving bandwith and time, or the new content of the file (with its new ETag).
This way if you change a single file in your project the user will not have to download the full content of every other module. It would be wise to add a short max-age header too, so that if the same module is requested twice in a short time there won't be additional requests.
If you add cache busting (e.g. appending ?x={randomNumber} through a bundler, or adding the checksum to every file name) you will force the user to download the full content of every necessary file at every new project version.
In both scenario you are going to do a request for each file anyway (the imported files on cascade will produce new requests, which at least may end in small 304 if you use etags). To avoid that you can use dynamic imports e.g if (userClickedOnSomethingAndINeedToLoadSomeMoreStuff) { import('./someModule').then('...') }
From my point of view dynamic imports could be a solution here.
Step 1)
Create a manifest file with gulp or webpack. There you have an mapping like this:
export default {
"/vendor/lib-a.mjs": "/vendor/lib-a-1234.mjs",
"/vendor/lib-b.mjs": "/vendor/lib-b-1234.mjs"
};
Step 2)
Create a file function to resolve your paths
import manifest from './manifest.js';
const busted (file) => {
return manifest[file];
};
export default busted;
Step 3)
Use dynamic import
import busted from '../busted.js';
import(busted('/vendor/lib-b.mjs'))
.then((module) => {
module.default();
});
I give it a short try in Chrome and it works. Handling relative paths is tricky part here.
I've created a Babel plugin which adds a content hash to each module name (static and dynamic imports).
import foo from './js/foo.js';
import('./bar.js').then(bar => bar());
becomes
import foo from './js/foo.abcd1234.js';
import('./bar.1234abcd.js').then(bar => bar());
You can then use Cache-control: immutable to let UAs (browsers, proxies, etc) cache these versioned URLs indefinitely. Some max-age is probably more reasonable, depending on your setup.
You can use the raw source files during development (and testing), and then transform and minify the files for production.
what i did was handle the cache busting in webserver (nginx in my instance)
instead of serving
<script src="scripts/main.js" type="module"></script>
serve it like this where 123456 is your cache busting key
<script src="scripts/123456/main.js" type="module"></script>
and include a location in nginx like
location ~ (.+)\/(?:\d+)\/(.+)\.(js|css)$ {
try_files $1/$2.min.$3 $uri;
}
requesting scripts/123456/main.js will serve scripts/main.min.js and an update to the key will result in a new file being served, this solution works well for cdns too.
Just a thought at the moment but you should be able to get Webpack to put a content hash in all the split bundles and write that hash into your import statements for you. I believe it does the second by default.
You can use an importmap for this purpose. I've tested it at least in Edge. It's just a twist on the old trick of appending a version number or hash to the querystring. import doesn't send the querystring onto the server but if you use an importmap it will.
<script type="importmap">
{
"imports": {
"/js/mylib.js": "/js/mylib.js?v=1",
"/js/myOtherLib.js": "/js/myOtherLib.js?v=1"
}
}
</script>
Then in your calling code:
import myThing from '/js/mylib.js';
import * as lib from '/js/myOtherLib.js';
You can use ETags, as pointed out by a previous answer, or alternatively use Last-Modified in relation with If-Modified-Since.
Here is a possible scenario:
The browser first loads the resource. The server responds with Last-Modified: Sat, 28 Mar 2020 18:12:45 GMT and Cache-Control: max-age=60.
If the second time the request is initiated earlier than 60 seconds after the first one, the browser serves the file from cache and doesn't make an actual request to the server.
If a request is initiated after 60 seconds, the browser will consider cached file stale and send the request with If-Modified-Since: Sat, 28 Mar 2020 18:12:45 GMT header. The server will check this value and:
If the file was modified after said date, it will issue a 200 response with the new file in the body.
If the file was not modified after the date, the server will issue a304 "not modified" status with empty body.
I ended up with this set up for Apache server:
<IfModule headers_module>
<FilesMatch "\.(js|mjs)$">
Header set Cache-Control "public, must-revalidate, max-age=3600"
Header unset ETag
</FilesMatch>
</IfModule>
You can set max-age to your liking.
We have to unset ETag. Otherwise Apache keeps responding with 200 OK every time (it's a bug). Besides, you won't need it if you use caching based on modification date.
A solution that crossed my mind but I wont use because I don't like it LOL is
window.version = `1.0.0`;
let { default: fu } = await import( `./bar.js?v=${ window.version }` );
Using the import "method" allows you to pass in a template literal string. I also added it to window so that it can be easily accessible no matter how deep I'm importing js files. The reason I don't like it though is I have to use "await" which means it has to be wrapped in an async method.
If you are using Visual Studio 2022 and TypeScript to write your code, you can follow a convention of adding a version number to your script file names, e.g. MyScript.v1.ts. When you make changes and rename the file to MyScript.v2.ts Visual Studio shows the following dialog similar to the following:
If you click Yes it will go ahead and update all the files that were importing this module to refer to MyScript.v2.ts instead of MyScript.v1.ts. The browser will notice the name change too and download the new modules as expected.
It's not a perfect solution (e.g. if you rename a heavily used module, a lot of files can end up being updated) but it is a simple one!
this work for me
let url = '/module/foo.js'
url = URL.createObjectURL(await (await fetch(url)).blob())
let foo = await import(url)
I came to the conclusion that cache-busting should not be used with ES Module.
Actually, if you have the versioning in the URL, the version is acting like a cache-busting. For instance https://unpkg.com/react#18.2.0/umd/react.production.min.js
If you don't have versioning in the URL, use the following HTTP header Cache-Control: max-age=0, no-cache to force the browser to always check if a new version of the file is available.
no-cache tells the browser to cache the file but to always perform a check
no-store tells the browser to don't cache the file. Don't use it!
Another approach: redirection
unpkg.com solved this problem with HTTP redirection.
Therefore it is not an ideal solution because it involves 2 HTTP requests instead of 1.
The first request is to get redirected to the latest version of the file (not cached, or cached for a short period of time)
The second request is to get the JS file (cached)
=> All JS files include the versioning in the URL (and have an aggressive caching strategy)
For instance https://unpkg.com/react#18.2.0/umd/react.production.min.js
=> Removing the version in the URL, will lead to a HTTP 302 redirect pointing to the latest version of the file
For instance https://unpkg.com/react/umd/react.production.min.js
Make sure the redirection is not cached by the browser, or cached for a short period of time. (unpkg allows 600 seconds of caching, but it's up to you)
About multiple HTTP requests: Yes, if you import 100 modules, your browser will do 100 requests. But with HTTP2 / HTTP3, it is not a problem anymore because all requests will be multiplexed into 1 (it is transparent for you)
About recursion:
If the module you are importing also imports other modules, you will want to check about <link rel="modulepreload"> (source Chrome dev blog).
The modulepreload spec actually allows for optionally loading not just the requested module, but all of its dependency tree as well. Browsers don't have to do this, but they can.
If you are using this technic in production, I am deeply interested to get your feedback!
Append version to all ES6 imports with PHP
I didn't want to use a bundler only because of this, so I created a small function that modifies the import statements of all the JS files in the given directory so that the version is at the end of each file import path in the form of a query parameter. It will break the cache on version change.
This is far from an ideal solution, as all JS file contents are verified by the server on each request and on each version change the client reloads every JS file that has imports instead of just the changed ones.
But it is good enough for my project right now. I thought I'd share.
$assetsPath = '/public/assets'
$version = '0.7';
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($assetsPath, FilesystemIterator::SKIP_DOTS) );
foreach ($rii as $file) {
if (pathinfo($file->getPathname())['extension'] === 'js') {
$content = file_get_contents($file->getPathname());
$originalContent = $content;
// Matches lines that have 'import ' then any string then ' from ' and single or double quote opening then
// any string (path) then '.js' and optionally numeric v GET param '?v=234' and '";' at the end with single or double quotes
preg_match_all('/import (.*?) from ("|\')(.*?)\.js(\?v=\d*)?("|\');/', $content, $matches);
// $matches array contains the following:
// Key [0] entire matching string including the search pattern
// Key [1] string after the 'import ' word
// Key [2] single or double quotes of path opening after "from" word
// Key [3] string after the opening quotes -> path without extension
// Key [4] optional '?v=1' GET param and [5] closing quotes
// Loop over import paths
foreach ($matches[3] as $key => $importPath) {
$oldFullImport = $matches[0][$key];
// Remove query params if version is null
if ($version === null) {
$newImportPath = $importPath . '.js';
} else {
$newImportPath = $importPath . '.js?v=' . $version;
}
// Old import path potentially with GET param
$existingImportPath = $importPath . '.js' . $matches[4][$key];
// Search for old import path and replace with new one
$newFullImport = str_replace($existingImportPath, $newImportPath, $oldFullImport);
// Replace in file content
$content = str_replace($oldFullImport, $newFullImport, $content);
}
// Replace file contents with modified one
if ($originalContent !== $content) {
file_put_contents($file->getPathname(), $content);
}
}
}
$version === null removes all query parameters of the imports in the given directory.
This adds between 10 and 20ms per request on my application (approx. 100 JS files when content is unchanged and 30—50ms when content changes).
Use of relative path works for me:
import foo from './foo';
or
import foo from './../modules/foo';
instead of
import foo from '/js/modules/foo';
EDIT
Since this answer is down voted, I update it. The module is not always reloaded. The first time, you have to reload the module manually and then the browser (at least Chrome) will "understand" the file is modified and then reload the file every time it is updated.
I'm creating my first nodejs/sails.js project, I want to use 3 different layout for 3 different occasions:
frontend_layout.ejs
admin_layout.ejs
mobile_layout.ejs
In frontend_layout.ejs, I want to load bootstrap.css, jquery.js and
bootstrap.js.
In admin_layout.ejs, I want to load bootstrap.css, angular.js and
ui-bootstrap-tpls.js(angular-ui js library).
In mobile_layout.ejs, I want to load ionic.css and ionic.bundle.js
I have created 3 folders in sailsProject/views/ which are admin_pages, mobile_pages and frontend_pages, the 3 layout.ejs files reside in each of these folders respectively, but no matter which layout I load, it always include all the css/js files in assets/js and assets/styles. I know I need to do something to pipeline.js, but how exactly? I'm not efficient at grunt, so I would really appreciate if anyone could point me which config files need to be modified and how... Thanks!
I wanted something very similar in my project, except I also wanted to take advantage of Sail's cool built-in ability to auto minimize/uglify javascript files for "sails lift --prod" in various layouts with different sets of javascript files. This answer only deals with the JS files, but you can make similar changes to support the same concept with your CSS files.
In my project I had 2 different layouts -- layout.ejs and layoutadmin.ejs. I created a new /assets/jsadmin folder which holds my admin javascript files. I left the sails existing /assets/js folder as-is to hold the javascript files for the public web pages.
My goal was for the /assets/js folder contents to be inserted between these tags (sails does this by default and these tags are used in the layout.ejs file):
<!--SCRIPTS-->
<!--SCRIPTS END-->
While the /assets/jsadmin folder contents was to be inserted between these tags (I made up these "custom" tag names and they are used in the layoutadmin.ejs file. I will add add support for this new tag in the rest of this answer):
<!--SCRIPTS_ADMIN-->
<!--SCRIPTS_ADMIN END-->
I created a full code sample demo of this here.
For development...
(sails lift), I modified so sails would populate my custom tags with the assets/jsadmin js files upon lifting.
I modified tasks/pipeline.js by adding a new variable called jsAdminFilesToInject which is very similar to the existing jsFilesToInject except it collects the js files from the jsAdmin folder.
var jsAdminFilesToInject = [
// Load sails.io before everything else
//'jsAdmin/dependencies/sails.io.js',
// Dependencies like jQuery, or Angular are brought in here
'jsAdmin/dependencies/**/*.js',
// All of the rest of your client-side js files
// will be injected here in no particular order.
'jsAdmin/**/*.js'
];
Note: I also had to export this new variable at the bottom of the pipeline.js file.
module.exports.jsAdminFilesToInject = jsAdminFilesToInject.map(function(path) {
return '.tmp/public/' + path;
});
I modified tasks/config/sails-linker.js by adding a new devJsAdmin task where it looks for tags and calls the new .jsAdminFilesToInject added in the pipeline.js file above.
devJsAdmin: {
options: {
startTag: '<!--SCRIPTS_ADMIN-->',
endTag: '<!--SCRIPTS_ADMIN END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': require('../pipeline').jsAdminFilesToInject,
'views/**/*.html': require('../pipeline').jsAdminFilesToInject,
'views/**/*.ejs': require('../pipeline').jsAdminFilesToInject
}
},
I Added a new task step to the tasks/register/linkAssets.js file which calls the devJsAdmin added above.
'sails-linker:devJsAdmin',
To test, run sails in demo mode:
sails lift
Browse to http://localhost:1337/home - you will see it is using the layout.ejs template and viewing the source will show the following at the bottom (files pulled from js folder):
<!--SCRIPTS-->
<script src="/js/dependencies/sails.io.js"></script>
<script src="/js/jquery-1.10.2.js"></script>
<!--SCRIPTS END-->
Browse to http://localhost:1337/admin - you will see it is using the layoutadmin.ejs template and viewing the source will show the following at the bottom of the source (files pulled from jsAdmin folder):
<!--SCRIPTS_ADMIN-->
<script src="/jsAdmin/dependencies/jquery-1.10.2.js"></script>
<script src="/jsAdmin/knockout-3.3.0.debug.js"></script>
<!--SCRIPTS_ADMIN END-->
For production...
(sails lift --prod), I wanted to do the same as development except I first wanted to concat and uglify the production javascript that goes in my new SCRIPTS_ADMIN tags.
I added a new jsAdmin section in the grunt tasks/config/concat.js file which pulls in the files from the previously added jsAdminFilesToInject in the pipeline.js to produce a concat/productionAdmin.js output file.
jsAdmin: {
src: require('../pipeline').jsAdminFilesToInject,
dest: '.tmp/public/concat/productionAdmin.js'
},
I added a new distAdmin section in the grunt tasks/config/uglify.js file which makes the concat/productionAdmin.js "ugly" by producing a new min/productionAdmin.min.js file.
distAdmin: {
src: ['.tmp/public/concat/productionAdmin.js'],
dest: '.tmp/public/min/productionAdmin.min.js'
}
I added a new prodJSAdmin section in the tasks/config/sails-linker.js file which adds the min/productionAdmin.min.js file between the SCRIPTS_ADMIN tags.
prodJsAdmin: {
options: {
startTag: '<!--SCRIPTS_ADMIN-->',
endTag: '<!--SCRIPTS_ADMIN END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': ['.tmp/public/min/productionAdmin.min.js'],
'views/**/*.html': ['.tmp/public/min/productionAdmin.min.js'],
'views/**/*.ejs': ['.tmp/public/min/productionAdmin.min.js']
}
},
Finally, I called this new prodJSAdmin from the prod grunt task by adding a line in the prod.js file.
'sails-linker:prodJsAdmin',
Run sails in production mode:
sails lift --prod
Browse to http://localhost:1337/home - you will see it is using the layout template and viewing the source will show the following at the bottom (using production.min.js):
<!--SCRIPTS-->
<script src="/min/production.min.js"></script>
<!--SCRIPTS END-->
Browse to http://localhost:1337/admin - you will see it is using the layoutadmin.ejs template and viewing the source will show the following at the bottom of the source (using productionAdmin.min.js):
<!--SCRIPTS_ADMIN-->
<script src="/min/productionAdmin.min.js"></script>
<!--SCRIPTS_ADMIN END-->
By default, Sails automatically insert all your css files (assets/styles) into tags between STYLES and STYLES END and js files (assets/js) into tags between SCRIPTS and SCRIPTS END.
<!--STYLES-->
<!--STYLES END-->
.
.
.
<!--SCRIPTS-->
<!--SCRIPTS END-->
This is set in pipeline.js file. By default it has set to get all css files from assets/styles. You can find it in cssFilesToInject section.
'styles/**/*.css'
You can change it as you wish. you can comment or delete it simply. (keep in mind if you want to put some css files common to every layout you can put them in here.)
Same for the js files. By default it has set to get all js files from assets/js. You can find it in jsFilesToInject section. Remove or add js files according to your requirement. You can find more information about grunt globbing patterns in here which helps to understand filtering pattern.
So easiest thing you can do now is put your layout specific files out side those tags(STYLES and SCRIPTS)
For example look following code sample,
<!--STYLES-->
<!--STYLES END-->
<!--STYLES SPECIFIC TO THIS LAYOUT-->
<link rel="stylesheet" href="/styles/some_layout_specific.css">
In php you can use headers to force downloads of files, and also to hide actual file locations etc.
This is useful if you only want certain users under certain conditions to be able to download certain files.
How would I do this in meteor? I've played around with the Node.js fs module, and managed to retrieve a binary version of the file on the client. But how would I turn this to an actual file that's downloaded?
Thanks!
Three easy steps:
use meteorite
add the iron-router package: mrt add iron-router
create a server method to serve your file. Here is how an exemple:
Router.map(function () {
this.route('get-image', {
where: 'server',
path: '/img',
action: function () {
console.log('retrieving ' + this.request.query.name);
this.response.writeHead(200, {'Content-type': 'image/png'}, this.request.query.name);
this.response.end(fs.readFileSync(uploadPath + this.request.query.name));
}
});
});
In this example the request is a HTTP GET with one parameter name=name-of-pdf.pdf.
That's really all. Hope it was what you were looking for.