EJS Statements in Plain HTML File and in TypeScript - express

I'm trying to wrap my head around a seed project for Angular 2 which has a lot of moving parts, and I came across something that I don't understand... In the index.html there are what appear to be ejs statements:
<!-- src/client/index.html -->
<title><%= APP_TITLE %></title>
Although I understand WHAT it's doing (allowing the title to be defined in a config file), I don't understand HOW it's doing it. While I do see express, I don't see ejs as a dependency in the package.json
To make it more confusing, there are similar ejs-like statements in some typescript files that look like this:
// src/client/app/system-config.ts
System.config(JSON.parse('<%= SYSTEM_CONFIG_DEV %>'));
// src/client/app/app.module.ts
#NgModule({
imports: [BrowserModule, HttpModule, RouterModule.forRoot(routes), AboutModule, HomeModule, SharedModule.forRoot()],
declarations: [AppComponent],
providers: [{
provide: APP_BASE_HREF,
useValue: '<%= APP_BASE %>' // <------- this!?!?
}],
bootstrap: [AppComponent]
})
So, this same interpolation is working outside of a template!? How does this work? What tool or package is facilitating this replacement? I can't figure it out. I believe this is the code for running the server:
/**
* Starts a new `express` server, serving the built files from `dist/prod`.
*/
export function serveProd() {
let root = resolve(process.cwd(), Config.PROD_DEST);
let server = express();
server.use(Config.APP_BASE, express.static(root));
server.use(fallback('index.html', { root }));
server.listen(Config.PORT, () =>
openResource('http://localhost:' + Config.PORT + Config.APP_BASE)
);
};
I know Express comes with EJS support out-of-the-box, however, it requires the file to have an .ejs extension. I know there is a way to force it to parse regular .html files, but that code does not seem exist in the angular seed project.

I finally figured out what it was just a few minutes after posting my question. Drumroll please...
It's gulp-template, not EJS. It's simply underscore templating that gulp is replacing with config values at build time. So, mystery solved.

Related

Embed script only on production with Vue CLI 3

My main goal is to inject a tag into my index.html only in production (it's a New Relic monitoring code snippet).
My Vue.js is built and served as a static resource, so using {% %} tags to surround the script block with a condition doesn't seem to work in this use case.
So I tried to add the New Relic code snippet on my Vue.js app using html-webpack-plugin, since I found a simple Webpack plugin using on html-webpack-plugin. It's a pretty simple plugin, it just create the node and pushes it in the page body : https://github.com/robrap/html-webpack-new-relic-plugin/blob/master/src/index.js#L25
I register the plugin by setting my vue.config.js this way (I first tried to add the script no matter the environment) :
var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackNewRelicPlugin = require('#yodatech/html-webpack-new-relic-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackNewRelicPlugin({the plugin options})
]
}
}
The plugin actually does its job well (the code snippet is injected), but its execution messes up with Vue CLI default configuration.
Some stylesheets and scripts aren't referenced anymore in the final index.html file, the <div id=app></div> is not there anymore, the app is broken.
I don't know if using HtmlWebpackPlugin is a dead end, but I currently don't know any other way to reach my goal.
Has anyone an idea on how I could make this work ?
Thanks a lot to anyone passing by.
EDIT : The plugin I was trying to use seemed to be flawed, I had to modify it to make it work with Vue CLI. My main problem has been solved by the selected answer.
vue.config.js option configureWebpack just merges the options you provide to a webpack config provided by Vue CLI. So by using your code, you are running 2 distinct HtmlWebpackPlugins (one from your config and one default from Vue CLI)
Try this instead:
var HtmlWebpackNewRelicPlugin = require('#yodatech/html-webpack-new-relic-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackNewRelicPlugin({the plugin options})
]
}
}

how to force clearing cache in chrome when release new Vue app version

I created an app with vue-cli and then I build the dist folder for production.
The app is deployed on IIS with flask backend and works fine.
The problem occurs when I have to make some changes and I have to redo the deployment. After this, users call me because app doesn't work but if I clear the chrome cache, the app works fine again.
How can I fix this problem? Is there a method to clear chrome cache automatically when I release a new application version?
Thanks
my dist folder
deployment: copy and paste folder dist on IIS
if files in dist folder are correct, maybe the problem is in axios cache? i have make some changes also to rest apis
I had the same problem and changing (incrementing) the version number in package.json before running the build command fixed it.
For example by default the version number is set to "0.1.0"
package.json file:
{
"name": "project-name",
"version": "0.1.1",
"private": true,
...
}
If you use vue-cli, then it has built-in webpack configs for building dist. And in fact it adds hash-names to output files.
But if it was removed somehow, you can add it back to webpack config like
output: {
filename: '[name].[hash].bundle.js'
}
And your app will looks like this:
And even more, you do not need to handle how all this stuff will be added to html, coz webpack will figure it out for you.
You need to add a version query to your js file. This is how a browser can know if the file has changed and needs to download the new version.
So something like:
<script src="main.js?v=1.1"></script>
<script src="main.js?v=1.2"></script>
etc...
Assuming this is nothing to do with service worker/PWA, the solution could be implemented by returning the front-end version.
axiosConfig.js
axios.interceptors.response.use(
(resp) => {
let fe_version = resp.headers['fe-version'] || 'default'
if(fe_version !== localStorage.getItem('fe-version') && resp.config.method == 'get'){
localStorage.setItem('fe-version', fe_version)
window.location.reload() // For new version, simply reload on any get
}
return Promise.resolve(resp)
},
)
You can also ensure the fe-version is returned based on any sort of uniqueness, here I have used the commit SHA.
Full Article here: https://blog.francium.tech/vue-js-cache-not-getting-cleared-in-production-on-deploy-656fcc5a85fe
You can't access the browser's cache, that would be huge a security flaw.
To fix it, you must send some headers with your flask responses telling the browser not to cache you app.
This is an example for express.js for you to get the idea:
setHeaders: function (res, path, stat) {
res.set('Cache-Control', 'no-cache, no-store, must-revalidate') // HTTP 1.1
res.set('Pragma', 'no-cache') // HTTP 1.0
res.set('Expires', '0') // Proxies
}
You can read a lot more about caching in here.
This is an older post, but since I could not find the solution for this problem online, ill just post this here in case someone else might find it usefull.
I added the hash to the appllication chunk files via the webpack.mix.js file by adding:
mix.webpackConfig({
output: {
chunkFilename: 'js/[name].js?id=[chunkhash]',
},
})
This adds a fingerprint to the actual chunks and not just the app.js file. You can add a version name to the app.js file aswell by adding version(['public/js/app.js']); at the end of the file, or add filename: '[name].js?[hash]' to the output block.
My complete webpack.mix.js:
const mix = require('laravel-mix');
mix.webpackConfig({
output: {
chunkFilename: 'js/main/[name].js?id=[chunkhash]',
}
}).js('resources/js/app.js', 'public/js').vue()
.postCss('resources/css/app.css', 'public/css', [
//
]).version(['public/js/app.js']);
In my laravel blade file I used
<script src="{{ mix('js/app.js') }}"></script>
to load the app.js file with the correct version fingerprint.
The answer for me was caching at my DNS provider level.
Basically, I'm using Cloudflare DNS proxy and they are caching the website so in development mode I was not getting the code updates.
I had to clear the cache many times to get anything to change. I had to wait a significant period of time before anything update.
Turned it off and it stopped doing that.
the method I want to suggest
<script src="{{ asset('js/app.js?time=') }}{{ time() }}" defer></script>
add below script in publc/index.html
<head>
...
<script type="text/javascript" language="javascript">
var timestamp = (new Date()).getTime();
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "<%= BASE_URL %>sample.js?t=" + timestamp;
document.head.appendChild(script);
</script>
...
</head>
could you try ?
vm.$forceUpdate();
Also it's possible that the component it self needs a unique key :
<my-component :key="unique" />

Vuelayers vl-style-icon syntax

I've been looking through the vuelayers documentation and have found little info on to use the vl-style-icon module, which is quite important if you want to create icons on your vuelayer map.
I'm pretty sure I have proper syntax when it comes to using it but marker.png won't load in through it. I've tried accessing it as just a normal image and it works fine so it is to my assumption that it's something with my syntax.
Here is my code:
<template>
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true" style="height: 400px">
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation" projection="EPSG:4326"></vl-view>
<vl-feature v-for="crime in crimePoints" :key="crime.id">
<vl-geom-point :coordinates="crime.coords"></vl-geom-point>
<vl-style-box>
<vl-style-icon src="./marker.png" :scale="0.4" :anchor="[0.5, 1]"></vl-style-icon>
</vl-style-box>
</vl-feature>
<vl-layer-tile>
<vl-source-osm></vl-source-osm>
</vl-layer-tile>
</vl-map>
</template>
vl-style-box and vl-style-icon are the main points here. I have also checked to see if the points come up without vl-style-box and they do. What could be wrong with my code?
You can try like this:
<vl-style-icon :src="require('./marker.png')" :scale="0.4" :anchor="[0.5, 1]"></vl-style-icon>
</vl-style-box>
If you used Vue CLI to create your vue project include this in your vue.config.js file. First section tells webpack to parse url attribute on custom tags other than what is already configured (Source).
module.exports = {
chainWebpack: config => {
config.module.rule('vue').use('vue-loader').tap(options => {
options.transformAssetUrls = {
'vl-style-icon': 'src',
...options.transformAssetUrls,
};
return options;
});
}
}
Run the following command to verify the correct vue-loader configuration is there
Source
vue inspect > output.js

Dojo amd loading cross-domain modules at runtime

I want to load Dojo1.9 amd modules from an ad-hoc server on the www, but I won't know from where until runtime (with url params).
In essence, I want to do the equivalent of this:
require(['http://www.foo.com/SomeRandomModule'], function( SomeRandomModule ) {
// use SomeRandomModule
});
Quick and dirty way
Might have some unexpected quirks when it comes to the module system and relative paths, I haven't used it enough to say:
require([ "//host/myext/mod1/mod2.js" ],function(mod2){
// If current webpage is http:// or https:// or file://
// it tries to use the same protocol
});
Better way
Configure require() to treat all modules that start with a certain package name (e.g. foo) as coming from a particular URL. From your starter page, something like:
<script src="dojo/dojo.js"
data-dojo-config="packages:[{name:'myext',location:'//host/js/myext'}], async: 1, >
</script>
This lets you vastly improve your first example to:
require([ "myext/mod1/mod2" ],function(mod2){
});
If you are using a Dojo Bootstrap installation instead, you can avoid touching your data-dojo-config and instead put it inside the run.js startup file:
require({
baseUrl: '',
packages: [
'dojo',
'dijit',
'dojox',
'myapp',
{ name: 'myext', location: '//host/js/myext', map: {} }
]
}, [ 'myapp' ]);

Simply cannot make SignalR (asp.net mvc4) and require.js work together

I've seen similar posts around the web and nothing anyone has suggested works for me. I'm really faced with the choice of dumping one or the other it seems at this point.
This "Getting Started with SignalR and MVC 4 tutorial":
http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc-4
says you need two script includes to make signalR work:
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-1.0.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
I'm at a loss as to how to make the second one, the autogenerated SignalR hub script, happen in require.js. Unless I'm missing something there just doesn't seem to be a viable require.js syntax for inclusion of autogenerated scripts. Without it you get this error at line 159 of jquery.signalR-1.1.2.js:
"JavaScript runtime error: SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. "
The code at that point in jquery.signalR is doing this:
signalR.hub = {
start: function () {
// This will get replaced with the real hub connection start method when hubs is referenced correctly
throw new Error("SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. <script src='/signalr/hubs'></script>.");
}
};
Has anyone actually made this autogenerated script thing happen via require.js?
Been studying this a bit more. Let me add some detail:
I'm using this approach - Structuring scalable client side applications: (http://johndavidmathis.wordpress.com/2013/04/23/structuring-scalable-client-side-applications/) to make a more scalable structure. Second part in that series "Permit modules to utilize multiple files and a logical folder structure" http://johndavidmathis.wordpress.com/2013/04/23/structuring-scalable-client-side-applications/ has me splitting my actual signalr code out into a separate Marionette chat module (separate from my main app.js file) to achieve a better file structure. I really like this approach. The rest of my project is set up like this now and it really is showing benefits when it comes to finding code. I think that extra split is where I'm stuck. Can't seem to get that second dependency, the autogenerated script, into that separate chat module file. I'm still studying this but it looks like this to me at this point. require.js gets the dependency into my Marionette app:
require(["marionette","handlebars", "signalr", "signalr.hubs"], function (Marionette) {
window.App = new Marionette.Application();
App.addRegions({
headerRegion: "#header",
contentRegion: "#content",
footerRegion: "#footer",
});
require(["modules/main/loader", "modules/chat/loader"], function () {
App.start();
});
})
If I want chat that dependency to make its way further into the app, into the chat module in another file?
Something like?
define(dependencies,
function () {
App.module("ChatModule", function (ChatModule, App, Backbone, Marionette, $, _, "signalr.hubs", "signalr.hubs") {
// SignalR Proxy created on the fly
var chat = $.connection.chatHub;
// Start the connection
$.connection.hub.start();
//more chat code...
An update:
The answer below does work in my dev environment. But it does not work when I publish the code to a real production server.
When the code is published to a real production server (IIS 6.1 on Windows Server Enterprise 2008 R2) the browser console once again shows a "404" for the autogenerated reference.
Specifically, the console shows the "?" is being added into the reference path before ".js", like this...
http://mydomain.com/myapp/Scripts/application/signalr/hubs?.js...
Tried taking the "?" out but then it removes my app name from the path, like this...
http://mydomain.com/signalr/hubs.js.
I think what would get me there is the first one, without the "?", like...
http://mydomain.com/myapp/Scripts/application/signalr/hubs.js
I'm just not seeing how to make that happen.
FINAL UPDATE:
Final piece of the puzzle for production server is the site's virtual directory. Here's final code that worked for me. Thanks Raciel R for your help:
requirejs.config({
paths: {
//core
"jquery": "jquery-1.9.1",
"signalr": "jquery.signalR-1.1.2",
"signalr.hubs": "/productionservervirtualdirectory/signalr/hubs?"
},
shim: {
"jquery": {exports: "$"},
"signalr": { deps: ["jquery"] },
"signalr.hubs": { deps: ["signalr"] }
});
//Then all you have to do is to make signalr.hubs required in your modules. Ie:
require(["signalr.hubs"], function(){
//your code here
});
requirejs.config({
paths: {
//core
"jquery": "jquery-1.9.1",
"signalr": "jquery.signalR-1.1.2",
"signalr.hubs": "/signalr/hubs?"
},
shim: {
"jquery": {exports: "$"},
"signalr": { deps: ["jquery"] },
"signalr.hubs": { deps: ["signalr"] }
});
Then all you have to do is to make signalr.hubs required in your modules. Ie:
require(["signalr.hubs"], function(){
//your code here
});
I set up RequireJS successfully using #raciel-r's solution but I was still having problems with other JavaScript modules like karma that were also confused by the dynamic proxy. I converted the signalr proxy to a static file and used that with RequireJS instead:
Import Microsoft.AspNet.SignalR.Utils
Run packages/Microsoft.AspNet.SignalR.Utils.2.X.X/tools/signalr.exe
ghp /path:my/bin /o:path/to/scripts/server.js where /my/bin is the directory containing the assemblies with your SignalR Hubs.
Replace your reference in to /signalr/hubs with server:
requirejs.config({
paths: {
// ...
"signalr.hubs": "path/to/scripts/server"
},
// ....
If you are using the convenience methods of the generated proxy, you will also have to rewrite them (see How to create a physical file for the SignalR generated proxy)