File injection with gulp, pick only some files from folder - gulp

I use Gulp for injecting my js files into index.html. I have the following structure of my js resources:
js
|-ace-builds
|-src
|-ace.js
|-ext-beautify.js
|-ext-chromevox.js
|-ext-elastic_tabstops_lite.js
|-...
|-angular
|-angular.js
|-angular-ui-ace
|-ui-ace.js
|-...
The problem is that from the whole ace-builds folder I need to inject only one file ace.js. Of course, one of the possible solution is to list all injected files manually, but I would like to make the injector definition quite compact and flexible.
Questions is: is it possible with gulp to make such file injection, ingoring other files in the folder and if yes, how?
I tried the following code, also placing the lines (1) and (2) in reverse order, but it has not worked, the ace.js is not included.
gulp.task('index', function () {
var target = gulp.src('./app/html/index.html');
var sources = gulp.src([
'app/js/*.js',
'!app/js/ace-builds/**/*', // (1)
'app/js/ace-builds/src-noconflict/ace.js' // (2)
], { read: false });
return target.pipe(inject(sources))
.pipe(gulp.dest('./app/html'));
});

Nothing in your gulp.src's actually reads the folder that ace.js is in. Why not just include app/js/**/ace.js? With no negation. And, by the way, the negation will always be performed last no matter where you have it listed. That is why you never pick up ace.js.

Related

Forge Viewer property database userFunction not found due to Vue webpack mangling (terser)

I am using a userFunction to query the property database in a custom Forge Viewer extension. This works great while testing the site locally using npm run serve. However, when I deploy the website to the web (which uses npm run build), the function no longer executes. The error says: SyntaxError: Function statements require a function name. This is because, according to the documentation, the function executed through executeUserFunction has to be named userFunction.
Upon further inspection I discovered that this was because of Vue & Webpack's mangling feature (executed by terser-webpack-plugin), where it renames variables and removes function names to decrease file size.
I have tried many different things, from making the function part of the extension's class to moving it to the global JS scope, but nothing helped. I also tried to exclude objects.js (which is the name of the extension I wrote) from mangling, but this didn't work either.
How do I configure terser to stop mangling this one variable?
I eventually figured out a way to do this which worked:
Add the following to vue.config.js:
module.exports = {
...
chainWebpack: config => {
config.optimization.minimizer('terser').tap(args => {
// eslint-disable-next-line no-param-reassign
args[0].terserOptions.keep_fnames = true;
return args;
});
},
};
This will prevent terser from removing function names, and will make it so userFunction still works. Weird design choice by Autodesk to require a function name, but at least it works now :)

Gulp globbing excluding files then unexcluding not working as described

If I have the files
client/
a.js
bob.js
bad.js
And the gulp task
gulp.task('copy', function() {
return gulp.src(['client/*.js', '!client/b*.js', 'client/bad.js'])
.pipe(gulp.dest('public'));
});
then according to the documentation we should copy a.js and bad.js. However, when I run this with gulp v3.9.1, it only copies a.js.
Is this a known bug? Is there a way to do this?
It's not a bug, the documentation is just wrong. The newest version of gulp is gulp#3.9.1 which uses vinyl-fs#0.3.14. The behavior you're referring to wasn't introduced until vinyl-fs#1.0.0.
In fact, elsewhere the gulp docs explicitly state that glob ordering will be a new feature in gulp#4.0.0:
globs passed to gulp.src will be evaluated in order, which means this is possible gulp.src(['*.js', '!b*.js', 'bad.js']) (exclude every JS file that starts with a b except bad.js)
That means you could simply use to the current development version of gulp (gulpjs/gulp#4.0) and take advantage of the new feature. Note however that gulp 4.x is radically different from gulp 3.x when it comes to defining tasks.
One workaround would be to keep using gulp 3.x for tasks definitions, but use the newest version of vinyl-fs to create vinyl streams:
var vinylFs = require('vinyl-fs');
gulp.task('copy', function() {
return vinylFs.src(['client/*.js', '!client/b*.js', 'client/bad.js'])
.pipe(vinylFs.dest('public'));
});
And if you don't want to do that you can always use merge-stream to combine multiple streams into one stream:
var merge = require('merge-stream');
gulp.task('copy', function() {
return merge(gulp.src(['client/*.js', '!client/b*.js']),
gulp.src(['client/bad.js']))
.pipe(gulp.dest('public'));
});

Using ES6 `import` with CSS/HTML files in Meteor project: bug or feature?

I am currently learning Meteor and I found out something that intrigued me.
I can load HTML and CSS assets from a JS file using the import statement.
import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
import * as myApp from '../imports/hello/myapp.js';
This was a surprise to me so I ran to google but could not find this behavior documented in the specification for ES6 import or in Meteor's Docs.
So my questions are:
Can I rely on this behavior to build my apps?
Will my app will break when Meteor gets around to fix it -- if it's a bug --?
Notes
I am using Meteor v1.3, not sure if this works also with previous versions.
You can download the app to see this behavior from Github
After going through the implementation of the built files for my app
I found out why this works.
HTML
Files are read from the file system and their contents added to the global Template object, e.g.,
== myapp.html ==
<body>
<h1>Welcome to Meteor!</h1>
{{> hello}}
</body>
results in the following JS code:
Template.body.addContent((function () {
var view = this;
return [
HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n "),
Spacebars.include(view.lookupTemplate("hello"))
];
}));
Which is wrapped in a function with the name of the file as it's key:
"myapp.html": function (require, exports, module) {
Template.body.addContent((function () {
var view = this;
return [
HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n "),
Spacebars.include(view.lookupTemplate("hello"))];
}));
Meteor.startup(Template.body.renderToDocument);
Template.__checkName("hello");
Template["hello"] = new Template("Template.hello", (
function () {
var view = this;
return [
HTML.Raw("<button>Click Me</button>\n "),
HTML.P("You've pressed the button ",
Blaze.View("lookup:counter",
function () {
return Spacebars.mustache(view.lookup("counter"));
}), " times.")
];
}));
},
So all of our HTML is now pure JS code which will be included by using require like any other module.
CSS
The files are also read from the file system and their contents are embedded also in JS functions, e.g.
== myapp.css ==
/* CSS declarations go here */
body {
background-color: lightblue;
}
Gets transformed into:
"myapp.css": ["meteor/modules", function (require, exports, module) {
module.exports = require("meteor/modules").addStyles("/* CSS declarations go here */\n\nbody {\n background-color: lightblue;\n}\n");
}]
So all of our CSS is also now a JS module that's again imported later on by using require.
Conclusion
All files are in one way or another converted to JS modules that follow similar rules for inclusion as AMD/CommonJS modules.
They will be included/bundled if another module refers to them. And since all of them are transformed to JS code
there's no magic behind the deceitful syntax:
import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
They both are transpiled to their equivalent forms with require once the assets have been transformed to JS modules.
Whereas the approach of placing static assets in the imports directory is not mentioned in the official documentation,
this way of importing static assets works.
This seems to be at the core of how Meteor works so I'd bet this functionality is going to be there for a long while.
I don't know if to call this a feature maybe a more appropriate description is unexpected consequence but that would
only be true from the user's perspective, I assume the people who wrote the code understood this would happen and perhaps even
designed it purposely this way.
One of the features in Meteor 1.3 is lazy-loading where you place your files in the /imports folder and will not be evaluated eagerly.
Quote from Meteor Guide:
To fully use the module system and ensure that our code only runs when
we ask it to, we recommend that all of your application code should be
placed inside the imports/ directory. This means that the Meteor build
system will only bundle and include that file if it is referenced from
another file using an import.
So you can lazy load your css files by importing them from the /imports folder. I would say it's a feature.
ES6 export and import functionally are available in Meteor 1.3. You should not be importing HTML and CSS files if you are using Blaze, the current default templating enginge. The import/export functionality is there, but you may be using the wrong approach for building your views.

Yii2 assets and gulp

I learn Yii2 and tried to use gulp with it in way like described here. I created gulp config with following content:
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var cssMin = require('gulp-css');
var rename = require('gulp-rename');
var minimist = require('minimist');
var options = minimist(process.argv.slice(2), {string: ['src', 'dist']});
var destDir = options.dist.substring(0, options.dist.lastIndexOf("/"));
var destFile = options.dist.replace(/^.*[\\\/]/, '');
// Use `compress-js` task for JavaScript files 
gulp.task('compress-js', function () {
    return gulp.src(options.src)
        .pipe(uglify())
        .pipe(rename(destFile))
        .pipe(gulp.dest(destDir))
});
// Use `compress-css` task for CSS files
gulp.task('compress-css', function () {
    return gulp.src(options.src)
        .pipe(cssMin())
        .pipe(rename(destFile))
        .pipe(gulp.dest(destDir))
});
I use this file for my bundle:
<?php
namespace app\assets;
use yii\web\AssetBundle;
class AppAsset extends AssetBundle
{
    public $basePath = '#webroot';
    public $baseUrl = '#web';
    public $css = [
        'css/site.css',
    ];
    public $js = [
    ];
    public $jsOptions = [
        'position' => \yii\web\View::POS_HEAD
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}
And, after all, I use this config for asset command
<?php
Yii::setAlias('#webroot', str_replace('\\', '/',  __DIR__) . '/web');
Yii::setAlias('#web', '/');
return [
    'jsCompressor' => 'gulp compress-js --gulpfile gulpfile.js --src {from} --dist {to}',
    'cssCompressor' => 'gulp compress-css --gulpfile gulpfile.js --src {from} --dist {to}',
    'bundles' => [
        'app\assets\AppAsset'
    ],
    'targets' => [
        'all' => [
            'class' => 'yii\web\AssetBundle',
            'basePath' => '#webroot/assets',
            'baseUrl' => '#web/assets',
            'js' => 'combined-{hash}.js',
            'css' => 'combined-{hash}.css',
            'depends' => [
            ],
        ],
    ],
    // Asset manager configuration:
    'assetManager' => [
        'basePath' => '#webroot/assets',
        'baseUrl' => '#web/assets',
        'linkAssets' => true
    ],
];
But, when I try to run php yii asset assets-config.php config/assets-prod.php combined files and configuration file config/assets-prod.php generate, but in web/assets directory exists a lot of symlinks (or direct copies if linkAssets === true) of files and folders, which is bundle dependencies themselves. They generate via publish() method of AssetManager. Why Yii don't clean these folders and files after compilation process? Or maybe I do something wrong?
Your script or css files can depend on their sources. For instance you combine css files. One file (from folder "foldername" its important) have following rule
i.sprite-icons {
url(./icons.png)
}
yii concatenate files & move them into "/assests" folder. So Yii also modifies urls. As a result you have following css rule.
i.sprite-icons {
url(./foldername/icons.png)
}
Yii run compressor only after it concatenates files. In your case - gulp.

With gulp, are watch tasks guaranteed to run before other tasks?

I have a gulp watch task that uses gulp-sass to convert SASS to CSS. For development, separated CSS files are all I want.
For release, I have a min tasks that uses gulp-cssmin and concat to bundle and minify the CSS. However, the min task doesn't deal with the SASS files. It assumes they have already been converted to CSS.
Is this a valid assumption? What if an automated build gets the latest source (which does not have .scss files) and runs min? Is there a race condition here as to whether the watch task will run in time for min to pick up the SASS files?
If your min task is also watching the SASS files, your assumption is not necessarily valid. By default, all the tasks will run at the same time. You can however define dependencies in your task, so that a task does not start until another task is finished. This could solve your problem. The documentation about dependencies can be found here.
There are also two other solutions:
Watch the CSS files for the min task.
Run the logic in your min task at the end of the task that compiles the SASS.
Edward! Better use optional minification to prevent extra reading from the disk.
var env = require('minimist')(process.argv.slice(2));
var noop = require('readable-stream/passthrough').bind(null, { objectMode: true });
gulp.task('style', function () {
return gulp.src('...')
.pipe(process())
.pipe(env.min ? uglify() : noop())
.pipe(gulp.dest('...'))
});
Also I don't recommend these tools from gulp-util which will be deprecated
Even if gulp guaranteed that it would wait for watch tasks to complete before starting dependent tasks, a race could still occur during the interval from when a source file is changed until the OS notifies gulp. So I decided not to use the SASS → CSS transform output. For as infrequently as I need to build the minimized versions, there's no payoff for saving those milliseconds.
Instead I used stream-combiner2 to reuse the transform logic:
var combiner = require('stream-combiner2');
function scssTransform() {
return combiner(
sourcemaps.init(),
sass(),
sourcemaps.write());
}
I use the transform for my non-mimized "sandbox" for local development:
gulp.task('sandbox:css', function () {
return gulp.src(paths.scss)
.pipe(scssTransform())
.pipe(gulp.dest(dirs.styles));
});
My task for serving the site locally depends on and watches the task:
gulp.task('serve.sandbox', ['sandbox:css' /* ... more ... */], function () {
// ... Start the web server ...
gulp.watch(paths.scss, ['sandbox:css']);
// ... Watch more stuff ...
});
I also use the transform for my minimized deployment:
gulp.task('deploy:css', function () {
return gulp.src(paths.scss)
.pipe(scssTransform())
.pipe(concat(files.cssMin))
.pipe(cssmin())
.pipe(gulp.dest(dirs.deploy));
});