Gulp: Create HTML from template and inject - gulp

I'm creating HTMLs to inject there revisioned sources from templates as file.html.tpl.
So I have css.html.tpl which has following:
<!-- common:css -->
<!-- endinject -->
<!-- vendor:css -->
<!-- endinject -->
<!-- custom:css -->
<!-- endinject -->
and using this function which read that .html.tpl file Sync, then write in .html the template
exports.createHTMLfromTemplate = function (distDir, templateFileNames) {
for(var i = 0; i < templateFileNames.length; i++) {
var file = templateFileNames[i];
var tplData = fs.readFileSync(path.join(__dirname, file));
var distPath = path.join(distDir, file.slice(0, -4)); // remove .tpl
fs.writeFile(distPath, tplData, function (err) {
if (err) log(exports.error(err));
});
}
};
and then we should have ready .html files with those comments to inject, then we inject our revisioned files.
gulp.task('inject-js', function () {
var distPath = path.join(__dirname, paths.baseSrc)
helper.createHTMLfromTemplate(distPath, 'js.html.tpl')
var injected = gulp.src(distPath + 'js.html');
_.each(jsTasks, function (task) {
var injector = task.filename.substr(0, task.filename.length - 3);
var gulpSrc = gulp.src(task.paths.dest + injector + '*.js', {read: false});
injected
.pipe($gulp.inject(gulpSrc, {name: injector}))
});
return injected
.pipe(gulp.dest(paths.baseSrc))
.on('error', helper.handleError);
});
the result in js.html is following:
<!-- common:css -->
<link rel="stylesheet" href="/static/dist/common-fee68d20.css">
<!-- endinject -->
<!-- vendor:css -->
<link rel="stylesheet" href="/static/dist/vendor-7cd25cb9.css">
<!-- endinject -->
<!-- custom:css -->
<!-- endinject -->
as you can see, if I run this gulp inside of Vagrant (VM), it will not inject in some comments, when running outside Vagrant, e.g. using local gulp, it injects properly.
Why? :)

Full disclosure: I have not used Vagrant
Maybe we can narrow it down. gulp-inject works on the given glob path and:
takes a stream of source files, transforms each file to a string and
injects each transformed string into placeholders in the target stream
files.
If the injection is not taking place, then my first though is that it is not finding the files that it needs to inject.
Try using something like gulp-debug to see if the files that need to be injected actually make it into the pipe stream.
If in the Vagrant environment you are getting the files from source control instead of mapping to the same path as your local dev environment then it should work. Otherwise the vinyl paths may not be resolving to the correct directory.
The puzzling part is that it works, but only partially. If it is not a path issue and you are using reved files then the task responsible for generating the <!-- custom:css --> may be the culprit. That would make more sense.
EDIT
A note from gulp-inject:
Note: As of gulp-inject v4.0.0, NodeJS v4 or above is required. To use
gulp-inject for older versions of Node install a specific version: npm
install gulp-inject#3.
I have no reason to think that this may be the problem, but just in case cross check the Node version number in your local and VM environments.

Related

application can't find a .js file

I have a Play application. The UI of the application is in Angular. I have created a folder ui which is the top level Angular directory.
build.sbt
`ui-dev-build` := {
implicit val UIroot = baseDirectory.value / "ui"
if (runDevBuild != Success) throw new Exception("Oops! UI Build crashed.")
}
def runDevBuild(implicit dir: File): Int = ifUiInstalled(runScript("npm run build"))
package.json
"build": "ng build --output-path ../public/ui",
When the Angular application is build, I transfer the output to the public folder of the Play framework. From there, Play transfers the contacts of the public folder to target folder for deployment. In the index.html (homepage html file), I access angular by including the scripts created in Angular build.
<script src="#routes.Assets.versioned("ui/runtime.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/vendor.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/styles.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/main.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/scripts.js")" type="text/javascript"></script>
This works fine.
I want to use ng-ace2-editor in my application - https://github.com/fxmontigny/ng2-ace-editor. I have added it in package.json - "ng2-ace-editor": "0.3.9" and I can see that ng2-ace-editor directory is present in node_modules.
When I run the application, I get error
GET http://localhost:9000/mode-html.js net::ERR_ABORTED 404 (Not Found)
exports.loadScript # index.js:3802
exports.loadModule # index.js:4174
setMode # index.js:10152
push../node_modules/ng2-ace-editor/src/component.js.AceEditorComponent.setMode
I can't understand how to make my application find mode-html.js. The file is present at location "./node_modules/ace-builds/src-min/mode-html.js. I have added this path in "script":[] of package.json but I still get the error.
"scripts":[
"./node_modules/ace-builds/src-min/ace.js",
"./node_modules/ace-builds/src-min/theme-eclipse.js",
"./node_modules/ace-builds/src-min/mode-html.js"
]
Interestingly, things work if I include ace.js in the homepage file
<script src="#routes.Assets.versioned("ui/runtime.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/vendor.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/styles.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/main.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("ui/scripts.js")" type="text/javascript"></script>
<script src="#routes.Assets.versioned("javascripts/common/vendor/ace/src-min/ace.js")" type="text/javascript"></script> <!-- this makes things work-->
So I know that issue is that my mode-html.js file is not getting served and most likely it is the path resolution issue but I can't figure out what is it.
Further analysis shows that the following code in ace.js causes the error.
exports.loadModule = function(moduleName, onLoad) {
var module, moduleType;
if (Array.isArray(moduleName)) {
moduleType = moduleName[0];
moduleName = moduleName[1];
}
try {
module = require(moduleName);
} catch (e) {}
if (module && !exports.$loading[moduleName])
return onLoad && onLoad(module);
if (!exports.$loading[moduleName])
exports.$loading[moduleName] = [];
exports.$loading[moduleName].push(onLoad);
if (exports.$loading[moduleName].length > 1)
return;
var afterLoad = function() {
require([moduleName], function(module) {
exports._emit("load.module", {name: moduleName, module: module});
var listeners = exports.$loading[moduleName];
exports.$loading[moduleName] = null;
listeners.forEach(function(onLoad) {
onLoad && onLoad(module);
});
});
};
if (!exports.get("packaged"))
return afterLoad();
net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad);
reportErrorIfPathIsNotConfigured();
};
var reportErrorIfPathIsNotConfigured = function() {
if (
!options.basePath && !options.workerPath
&& !options.modePath && !options.themePath
&& !Object.keys(options.$moduleUrls).length
) {
console.error(
"Unable to infer path to ace from script src,",
"use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes",
"or with webpack use ace/webpack-resolver"
);
reportErrorIfPathIsNotConfigured = function() {};
}
};
Why does explicitly calling <script src="#routes.Assets.versioned("javascripts/common/vendor/ace/src-min/ace.js")" type="text/javascript"></script> make the code work. Should this script be already available in ui/scripts.js as per Angular packaging method https://upgradetoangular.com/angular-news/the-angular-cli-is-a-great-way-to-build-your-angular-app-but-what-it-does-can-be-a-mystery-what-are-those-files-it-generates/?
I finally was able to make my code work. My setup is different. I build my Angular application and the it is served from my Play server. The angular build is stored in Play's /public/ui folder. The requests should be in format /assets/ui/.. which gets mapped to /public/ui/... due to a rule in Play routes file
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
When I ran the code, I got error.
Uncaught DOMException: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at 'http://localhost:9000/worker-javascript.js' failed to load.
at blob:http://localhost:9000/3df21e42-fecb-4026-8bd6-f2b0d1d0540a:1:1
Earlier, I also got error Unable to infer path to ace from script src, use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes or with webpack use ace/webpack-resolver
It seems ng-ace-editor imports .js scripts (theme, mode, worker) based on the theme and mode of the editor. The theme and mode js files can be included in scripts.js but some worker-.js files can't be (I don't know why, maybe because worker ones are loaded dynamically using importScript.
The scripts section in Angular.json is (this will all get bundled in scripts.js in Angular's final bundle)
"scripts": [
"./node_modules/ace-builds/src/ace.js",
"./node_modules/ace-builds/src/theme-eclipse.js",
"./node_modules/ace-builds/src/theme-monokai.js",
"./node_modules/ace-builds/src/mode-html.js"
]]
To include worker-.js files, I added this rule because it seems angular-cli can't load from node_modules. So I had to copy the files from node modules to root of my ui build - How to include assets from node_modules in angular cli project
"assets": [
"src/assets",
"src/favicon.ico",
{
"glob": "**/*",
"input": "./node_modules/ace-builds/src/",
"output": "/"
}
],
When I executed the code, I found error that http://localhost:9000/worker-javascript.js can't be loaded. I realised that my files are loaded in /assets/ui/ path and not in the server's root directory. So I set the basepath to /assets/ui in the component's .ts file
import * as ace from 'ace-builds/src-noconflict/ace';
ace.config.set('basePath', '/assets/ui/');
ace.config.set('modePath', '');
ace.config.set('themePath', '');
In summary
basePath seem to be what is used to load scripts dynamically (eg worker scripts)
modePath and themePath are / as the mode and theme scripts are bundled in scripts.js and are available at root level
need to copy worker-.js files outside node_modules as angular_cli can't copy assets from node_modules

Programmatically loading a ES6 module with Traceur in web page

I have been using Traceur to develop some projects in ES6. In my HTML page, I include local Traceur sources:
<script src="traceur.js"></script>
<script src="bootstrap.js"></script>
and if I have a module in the HTML afterwards like:
<script type="module" src="foo.js"></script>
Then Traceur loads in that module, compiles it and everything works great.
I now want to programmatically add an ES6 module to the page from within another ES6 module (reasons are somewhat complicated). Here was my first attempt:
var module = document.createElement('script');
module.setAttribute('type', 'module');
module.textContent = `
console.log('Inside the module now!');
`;
document.body.appendChild(module);
Unfortunately this doesn't work as Traceur does not monitor the page for every script tag added, I guess.
How can I get Traceur to compile and execute the script? I guess I need to invoke something on either 'traceur' or '$traceurRuntime' but I haven't found a good online source of documentation for that.
You can load other modules using ES6 import statements or TraceurLoader API for dynamic dependencies.
Example from Traceur Documentation
function getLoader() {
var LoaderHooks = traceur.runtime.LoaderHooks;
var loaderHooks = new LoaderHooks(new traceur.util.ErrorReporter(), './');
return new traceur.runtime.TraceurLoader(loaderHooks);
}
getLoader().import('../src/traceur.js',
function(mod) {
console.log('DONE');
},
function(error) {
console.error(error);
}
);
Also, System.js loader seems to be supported as well
window.System = new traceur.runtime.BrowserTraceurLoader();
System.import('./Greeter.js');
Dynamic module loading is a (not-yet-standardized) feature of System:
System.import('./repl-module.js').catch(function(ex) {
console.error('Internal Error ', ex.stack || ex);
});
To make this work you need to npm test then include BrowserSystem
<script src="../bin/BrowserSystem.js"></script>
You might also like to look into https://github.com/systemjs/systemjs as it has great support for browser loading.
BTW the System object may eventually be standardize (perhaps under a different name) in the WHATWG: http://whatwg.github.io/loader/#system-loader-instance

Gulp-Rev-replace not replacing my the file path with the gulp-rev hash

Similar to this question here: How do I replace the filenames listed in index.html with the output of gulp-rev?
Im using gulp-useref to combine the files listed in my build blocks. I would like to use gulp-rev to add a hash to the end of the concatenated file and update my markup with the new filename.
As per the gulp-rev-replace doc, im using this almost exactly with minor tweaks:
var gulp = require('gulp');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');
var useref = require('gulp-useref');
var filter = require('gulp-filter');
var uglify = require('gulp-uglify');
var csso = require('gulp-csso');
var RevAll = require('gulp-rev-all');
gulp.task("index", function () {
var jsFilter = filter("js/**/*.js");
var cssFilter = filter("css/**/*.css");
var revAll = new RevAll();
var userefAssets = useref.assets();
return gulp.src("Views/**/*.cshtml")
.pipe(userefAssets) // Concatenate with gulp-useref
.pipe(jsFilter)
.pipe(uglify()) // Minify any javascript sources
.pipe(jsFilter.restore())
.pipe(cssFilter)
.pipe(csso()) // Minify any CSS sources
.pipe(cssFilter.restore())
.pipe(rev()) // Rename the concatenated files
.pipe(userefAssets.restore())
.pipe(useref())
.pipe(revReplace()) // Substitute in new filenames
.pipe(gulp.dest('public'));
});
my html looks like so:
<!-- build:css css/combined.css -->
<link href="css/components/style1.css" rel="stylesheet">
<link href="css/components/style2.css" rel="stylesheet">
<link href="css/pages/style3.css" rel="stylesheet">
<!-- endbuild -->
<!-- build:js js/lib.js -->
<script type="text/javascript" src="js/global/js1.js"></script>
<script type="text/javascript" src="js/vendor/moment.js"></script>
<script type="text/javascript" src="js/components/component.calendar.js"></script>
<script type="text/javascript" src="js/pages/jaunt-hotels/matrix.js"></script>
<!-- endbuild -->
the output I get is:
<link rel="stylesheet" href="css/combined.css">
<script src="js/lib.js">
the </script>
but what im looking for is something like:
<link rel="stylesheet" href="css/combined-ABC234.css">
<script src="js/lib-TYU765.js"></script>
directory structure:
root
|-css
|-style1.css
|-style2.css
|-style3.css
|-js
|-*js files here
|-views
|-*Many folders with .cshtml files
|-gulp
|-tasks
|-bundle-assets.js <- task that contains code above
Any help is greatly appreciated!
You need to pass an option to revReplace(), like so:
.pipe(revReplace({replaceInExtensions: ['.cshtml']}));
By default, gulp-rev-replace will perform changes only on ['.js', '.css', '.html', '.hbs'].
More information on https://www.npmjs.com/package/gulp-rev-replace#options-replaceinextensions.
I was working with .ejs and had a similar issue. Hope this helps..
Thanks for updating your question! I tried your Gulp-Setup yesterday and found out, that the useref and rev plugin's won't work if the path to your assets are incorrect. In your sample, the *.cshtml files are all in the folder views, but pointing to ressources which are on one level above. Going from a file inside views (like views/index.cshtml, the path css/components/style1.css cannot be resolved, because it should actually be ../css/components/style1.css. Therefore useref gets an empty resultset, does not create any files, and rev cannot update because it has no point to reference to.
Get the paths in your cshtml right so that they're relative to the real ressources, than it will work. I hope you can because that setup looks a little Dotnetty, and I'm not sure if you have freedom there to change things ;-)

Apply conditions in gulp.dest

I have a gulp task which gets the html file, finds the scripts inside it, minifies them, applies revisions and outputs everything in the assets folder:
templates/index.html
<!-- build:js js/app.js -->
<script src="lib/js/a.js"></script>
<script src="lib/js/b.js"></script>
<!--endbuild -->
gulpfile.js
var useref = require('gulp-useref'),
filter = require('gulp-filter'),
uglify = require('gulp-uglify'),
rev = require('gulp-rev'),
revReplace = require('gulp-rev-replace');
gulp.task('build',function() {
var assets = useref.assets({searchPath: './'}),
jsapp = filter('**/app.js'),
return gulp
.src(gulp.src('templates/index_src.html'))
// collect all assets from source file by the means of useref
.pipe(assets)
//build js/app.js
.pipe(jsapp)
.pipe(uglify())
.pipe(jsapp.restore())
// Take inventory of the file names for future rev numbers
.pipe(rev())
// Apply the concat and file replacement with useref
.pipe(assets.restore())
.pipe(useref())
// Replace the file names in the html with rev numbers
.pipe(revReplace())
// output files
.pipe(gulp.dest('./'));
});
This works fine but outputs everything (js/app.js and index.html) to the root directory (./);
Is there are any way to apply a condition inside the gulp.dest and output js/app.js to the root folder of the project but output index.html to a different location (e.g. ./templates_cache)?
Also add a filter for your html files, to be able to output only those files to a different folder:
var htmlFilter = filter('**/*.html')
Change your output to:
// output for js files
.pipe(jsapp)
.pipe(gulp.dest('./'))
.pipe(jsapp.restore())
// output for html files
.pipe(htmlFilter)
.pipe(gulp.dest('./templates_cache'));

Master page in HTML

Is there any way to create a similar idea as master/content page in ASP.NET in pure HTML?
I want to create several pages in HTML, but I want that all of them look the same with some contents different. Is there any way to do this without creating several pages which are very similar to each other?
//wait until the dom is loaded
$(document).ready(function () {
//adds menu.html content into any "#menu" element
$('#menu').load('menu.html');
});
In reference to some of the other answers, iframes should be used carefully and sparingly.
http://rev.iew.me/help-moving-from-iframes
http://fsvieira.com/2013/06/11/iframes-bad-for-a-website/
Duplicate question here with answer: How to create a master page using HTML?
The simple way to do that is to use server side includes or SSI. However easier and, probably, much better solution would be usage of PHP with includes. This way you will always have additional PHP functionality then you need it. But both of this solutions require server that will preprocess the pages. If you want collection of pages, say, on a local hard drive, then only solution I know is already proposed iframe tag.
You can use iframe. That would be purely html.
I resolved with a Third party c# form application.
Header and footer different page
insert key to all other page. (###footer###)
Replace files contents with form Application.
footer.html
<h2>this place is footer.</h2>
default.html
<h1>Default page</h1>
bla bla bla
###footer###
Result default.html
<h1>Default page</h1>
bla bla bla
<h2>this place is footer.</h2>
Source Code below
List list = new List();
private void sourceBtn_Click(object sender, EventArgs e)
{
DialogResult result = openFileDialog1.ShowDialog(this);
if (result == DialogResult.OK)
{
sourceTxt.Text = openFileDialog1.FileName;
}
}
private void fileListSelect_Click(object sender, EventArgs e)
{
var result = openFileDialog2.ShowDialog(this);
if (result == DialogResult.OK)
{
fileList.Items.AddRange(openFileDialog2.FileNames);
}
}
private void addSourceBtn_Click(object sender, EventArgs e)
{
list.Add(new sourceKey() { filename = sourceTxt.Text, key = keyTxt.Text });
sourceTxt.Clear();
keyTxt.Clear();
sourceTxt.Focus();
sourceList.DataSource = null;
sourceList.DataSource = list;
}
private void ConvertBtn_Click(object sender, EventArgs e)
{
foreach (var filename in fileList.Items)
{
string text = File.ReadAllText(filename.ToString());
foreach (var item in sourceList.DataSource as List)
{
text = text.Replace(item.key, File.ReadAllText(item.filename));
}
File.WriteAllText(filename.ToString(), text);
}
infoLabel.Text = "Done";
}
Source Code Download Link
Use javascript gulp tool
and it will be like this :
##include('./header.html')
<!-- Content -->
<section>
<h1>Hello world</h1>
</section>
##include('./footer.html')
One of the best ways to have the option of including repeating blocks is using Gulp.js and some packages . gulp is a popular javascript toolkit to automate & enhance your workflow .
for using it first install gulp in your project using yarn or npm :
yarn init
Install the gulp-file-include plugin :
yarn add gulp gulp-file-include -D
create gulpfile to be able to create tasks with Gulp
In linux :
touch gulpfile.js
if you are using windows use this command instead :
type "gulpfile.js"
In the gulpfile.js import gulp and gulp-file-include. you will also create a variable paths to define the path of source and the destination path (where the static html files will be after the build) :
const gulp = require('gulp');
const fileinclude = require('gulp-file-include');
const paths = {
scripts: {
src: './',
dest: './build/'
}
};
In gulpfile.js file , create a task function that will be responsible for including html files and returning static files:
async function includeHTML(){
return gulp.src([
'*.html',
'!header.html', // ignore
'!footer.html' // ignore
])
.pipe(fileinclude({
prefix: '##',
basepath: '#file'
}))
.pipe(gulp.dest(paths.scripts.dest));
}
For now set function as default :
exports.default = includeHTML;
Add the include tags to index.html:
##include('./header.html')
<!-- Content -->
<section>
<h1>Hello world</h1>
</section>
##include('./footer.html')
Run the gulp command :
yarn gulp
The build folder will be created with index.html file inside
Done :)
Well, just as an ugly solution, try <iframe> tags. They load remote pages into your website, so you could define a "master template" like this:
...
<body>
<div id="content">
<iframe src="content1.html"></iframe>
...
Now, inside of content1.html, you could just write the content without the main layout.