Webpack CopyPlugin : add file to plugin directory - npm

I'd like to add the current module folder during my webpack compilation to my dist/ directory. For now, in /dist, I have something like that
const toCopy = [
'./../../../../node_modules/flatpickr/dist/flatpickr.min.js',
'./../../../../node_modules/flatpickr/dist/flatpickr.min.css',
]
this is the part of my webpack CopyPlugin
...
plugins: [
new CopyPlugin({
patterns: toCopy.map((entry) => {
console.log(entry);
return { from: entry }
})
})
]
And it gives me that
/dist
flatpickr.min.css
flatpickr.min.css
And I'd like that in order to avoid conflict if files from different modules have the same name .
for example, flatpickr as a ie.css, fr.js... but some other module could also have those files
/dist
/flatpickr
flatpickr.min.css
flatpickr.min.css

I've ended by created a loop over all my assets to copy to keep the CopyPlugin structure
const toCopy = new Map();
toCopy.set('popper', './../../contrib/bootstrap_sass/js/popper.min.js',);
toCopy.set('flatpickr', ['./../../../../node_modules/flatpickr/dist/flatpickr.min.js',
'./../../../../node_modules/flatpickr/dist/flatpickr.min.css']);
let toCopyFormatted = [];
for (let [dest, entries] of toCopy.entries()) {
if (typeof entries === 'string') {
let data = {to: dest}
data['from'] = entries;
toCopyFormatted.push(data);
} else {
entries.forEach(function (v, k) {
let data = {to: dest}
data['from'] = v;
toCopyFormatted.push(data);
})
}
}

Related

Vue.js: Is it possible to do "Conditional compilation" with a Vue project?

In for example Swift/iOS development, it's possible to differentiate builds for different environments with "flags" such as:
#if STAGING
// one set of logic here
#endif
#if PRODUCTION
// another set of logic here
#endif
Is it possible to achieve the same with a Vue.js project, and how would we go about doing it? I am aware of makes different routes conditionally available for different roles (which is also quite neat), but I am optimally looking for the option to differentiate on a source code level.
Hope someone has some great insights! It could include:
How to exclude parts of a file (such as the #if STAGING above) from a build target
How to exclude entire files from a build target
etc.
you have the ability to use this syntax
if(process.env.NODE_ENV === 'production') {
console.log("this is the prod env!!!!!!!!!!");
config.output.path = path.resolve(__dirname, "dist");
}
make sure that when you run the script with the correct env's for each environment (local, dev, staging, prod etc ..) :D
just change the vue-loader output.
the source code
<template v-if="process.env.NODE_ENV === 'development'">
development only
</template>
default output
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"div",
{ attrs: { id: "app" } },
[
_vm.process.env.NODE_ENV === "development"
? [_vm._v(" development only ")]
: _vm._e(),
_c("router-view")
],
2
)
}
just use regex to replace _vm.process.env. by process.env is ok.
// webpack.config.js
module: {
rules: [{
// must set post
enforce: 'post',
test: /\.vue$/,
use: [{
loader: './myLoader'
}]
}]
}
// myLoader.js
module.exports = function (source, map) {
if (source.indexOf('_vm.process.env') > -1) {
source = source.replace(/_vm.process.env/g, 'process.env')
}
this.callback(
null,
source,
map
)
}
Final the vue-loader result change
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"div",
{ attrs: { id: "app" } },
[
// change to true
true
? [_vm._v(" development only ")]
: undefined,
_c("router-view")
],
2
)
}

How to remove attributes from tags inside Vue components?

I want to use data-test attributes (as suggested here), so when running tests I can reference tags using these attributes.
<template>
<div class="card-deck" data-test="container">content</div>
</template>
The element will be found using:
document.querySelector('[data-test="container"]')
// and not using: document.querySelector('.card-deck')
But I don't want these data-test attributes to get to production, I need to remove them. How can I do that? (There are babel plugins that do that for react.)
Thanks!
The solution (for a Nuxt.js project), provided by Linus Borg in this thread, is:
// nuxt.config.js
module.exports = {
// ...
build: {
// ...
extend(config, ctx) {
// ...
const vueLoader = config.module.rules.find(rule => rule.loader === 'vue-loader')
vueLoader.options.compilerModules = [{
preTransformNode(astEl) {
if (!ctx.dev) {
const {attrsMap, attrsList} = astEl
tagAttributesForTesting.forEach((attribute) => {
if (attrsMap[attribute]) {
delete attrsMap[attribute]
const index = attrsList.findIndex(x => x.name === attribute)
attrsList.splice(index, 1)
}
})
}
return astEl
},
}]
}
}
}
where tagAttributesForTesting is an array with all attributes to be removed, like: ["data-test", ":data-test", "v-bind:data-test"].
For those of you who want to know how to do this is vanilla Vue 3, read on.
According to the Vue CLI documentation, the correct way to override a loader's options is to use the chainWebpack method within your Vue configuration (within your vue.config.js file):
module.exports = {
chainWebpack(config) {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => {
options.compilerOptions.modules = [{
preTransformNode(element) {
if (process.env.NODE_ENV !== 'production') return
const { attrsMap, attrsList } = element;
if ('data-test' in attrsMap) {
delete attrsMap[attribute];
const index = attrsList.findIndex(x => x.name === attribute);
attrsList.splice(index, 1)
}
return element;
}
}];
return options;
});
}
};
For your particular use case, I think the most maintenance free option would be to use a pattern matching strategy to remove test attributes. This would keep you from having to add every new test attribute to the list of blacklisted attributes:
{
preTransformNode(element) {
if (process.env.NODE_ENV !== 'production') return
const { attrsMap, attrsList } = element;
for (const attribute in attrsMap) {
// For example, you could add a unique prefix to all of your test
// attributes (e.g. "data-test-***") and then check for that prefix
// using a Regular Expression
if (/^data-test/.test(attribute)) {
delete attrsMap[attribute];
const index = attrsList.findIndex(x => x.name === attribute);
attrsList.splice(index, 1)
}
}
return element;
}
}
Note that the attributes will include any Vue directives (e.g. "v-bind:") that you attach to them, so be sure to compensate for that if you decide to identify your test attributes using unique prefixes.
I think it would be best to mention that, just like #ahwo before me, I drew my inspiration from Linus Borg's suggestion on the Vue forums.
P.s. With Vue, it's possible to create attributes that have dynamic names. I think this would be useful to know for anyone who is adding attributes for testing

Aurelia CLI font-awesome

I've tried several different solutions but not a single one have worked for me.
I'm using.NET Core, latest Aurelia/Aurelia CLI and Font-Awesome installed using npm install font-awesome --save.
Solution 1:
New file: prepare-font-awesome.js in folder \aurelia_project\tasks
import gulp from 'gulp';
import merge from 'merge-stream';
import changedInPlace from 'gulp-changed-in-place';
import project from '../aurelia.json';
export default function prepareFontAwesome() {
const source = 'node_modules/font-awesome';
const taskCss = gulp.src(`${source}/css/font-awesome.min.css`)
.pipe(changedInPlace({ firstPass: true }))
.pipe(gulp.dest(`${project.platform.output}/css`));
const taskFonts = gulp.src(`${source}/fonts/*`)
.pipe(changedInPlace({ firstPass: true }))
.pipe(gulp.dest(`${project.platform.output}/fonts`));
return merge(taskCss, taskFonts);
}
Updated build.js\aurelia_project\tasks
import prepareFontAwesome from './prepare-font-awesome'; // Our custom task
export default gulp.series(
readProjectConfiguration,
gulp.parallel(
transpile,
processMarkup,
processCSS,
prepareFontAwesome // Our custom task
),
writeBundles
);
Included font-awesome in html
<link rel="stylesheet" href="scripts/css/font-awesome.min.css">
Error:
GET http://localhost:9000/scripts/css/font-awesome.min.css
Solution 2:
Updated aurelia.json
{
"name": "font-awesome",
"path": "../node_modules/font-awesome/",
"main": "",
"resources": [
"css/font-awesome.min.css"
]
}
Added font files in root/font-awesome/fonts
Included font-awesome in html
<require from="font-awesome/css/font-awesome.min.css"></require>
Error: No error but icons are not shown
Solution 3:
Updated build.js:
import gulp from 'gulp';
import transpile from './transpile';
import processMarkup from './process-markup';
import processCSS from './process-css';
import { build } from 'aurelia-cli';
import project from '../aurelia.json';
import fs from 'fs';
import readline from 'readline';
import os from 'os';
export default gulp.series(
copyAdditionalResources,
readProjectConfiguration,
gulp.parallel(
transpile,
processMarkup,
processCSS
),
writeBundles
);
function copyAdditionalResources(done){
readGitIgnore();
done();
}
function readGitIgnore() {
let lineReader = readline.createInterface({
input: fs.createReadStream('./.gitignore')
});
let gitignore = [];
lineReader.on('line', (line) => {
gitignore.push(line);
});
lineReader.on('close', (err) => {
copyFiles(gitignore);
})
}
function copyFiles(gitignore) {
let stream,
bundle = project.build.bundles.find(function (bundle) {
return bundle.name === "vendor-bundle.js";
});
// iterate over all dependencies specified in aurelia.json
for (let i = 0; i < bundle.dependencies.length; i++) {
let dependency = bundle.dependencies[i];
let collectedResources = [];
if (dependency.path && dependency.resources) {
// run over resources array of each dependency
for (let n = 0; n < dependency.resources.length; n++) {
let resource = dependency.resources[n];
let ext = resource.substr(resource.lastIndexOf('.') + 1);
// only copy resources that are not managed by aurelia-cli
if (ext !== 'js' && ext != 'css' && ext != 'html' && ext !== 'less' && ext != 'scss') {
collectedResources.push(resource);
dependency.resources.splice(n, 1);
n--;
}
}
if (collectedResources.length) {
if (gitignore.indexOf(dependency.name)< 0) {
console.log('Adding line to .gitignore:', dependency.name);
fs.appendFile('./.gitignore', os.EOL + dependency.name, (err) => { if (err) { console.log(err) } });
}
for (let m = 0; m < collectedResources.length; m++) {
let currentResource = collectedResources[m];
if (currentResource.charAt(0) != '/') {
currentResource = '/' + currentResource;
}
let path = dependency.path.replace("../", "./");
let sourceFile = path + currentResource;
let destPath = './' + dependency.name + currentResource.slice(0, currentResource.lastIndexOf('/'));
console.log('Copying resource', sourceFile, 'to', destPath);
// copy files
gulp.src(sourceFile)
.pipe(gulp.dest(destPath));
}
}
}
}
}
function readProjectConfiguration() {
return build.src(project);
}
function writeBundles() {
return build.dest();
}
Updated aurelia.json
{
"name": "font-awesome",
"main":"",
"path": "../node_modules/font-awesome",
"resources": [
"css/font-awesome.css",
"/fonts/fontawesome-webfont.woff2",
"/fonts/FontAwesome.otf",
"/fonts/fontawesome-webfont.eot",
"/fonts/fontawesome-webfont.svg",
"/fonts/fontawesome-webfont.ttf"
]
}
Included font-awesome in html
<require from="font-awesome/css/font-awesome.css"></require>
Error:
get:
http://localhost:9000/font-awesome/fonts/fontawesome-webfont.woff2?v=4.7.0
(same with woff and ttf)
It's really strange because the files are copied and the url is correct..
Folder structure:
Tested a couple of different sources
What am I missing?
I would prefer a less implementation so I can import Font-Awesome in my master less file.
Based off of the discussion, since you are hosting your project inside the wwwroot folder, you must base your "gets" for files from there.
So, if you move your font files into wwwroot/fonts/font-name.woff (or somewhere thereabouts), you should be golden.
If you are on a webpack based project generated with latest (2019ish) aurelia cli , then adding fontawesome or bootstrap is pretty simple.
step 1: install fontawesome
check the official docs here. Just for completeness, here is the npm or yarn way for free version
//with npm
npm install --save-dev #fortawesome/fontawesome-free
// yarn
yarn add --dev #fortawesome/fontawesome-free
step 2: import the font
in your main.js or main.ts or app.js or app.ts , all of them will work equally well, which is better? ask google.
import '#fortawesome/fontawesome-free/css/all.min.css';
And an even simpler method would be to add the CDN version into the head of your index.esj or index.html file
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.1/css/all.css" />
all of the above work equally well, personally for public apps, I prefer CDN solution due to browser cache.

How do I integrate PostCSS into Broccoli angular-cli Build?

It seems like this build script is running but the CSS that is output is not minified or auto prefixed. I am attempting to compile SASS, then run the output through Post CSS with Broccoli and the angular-cli. I figure maybe some ember-cli folks could help as well. What am I doing wrong?
The build outputs this in terminal:
Slowest Trees | Total
----------------------------------------------+---------------------
BroccoliTypeScriptCompiler | 1274ms
vendor | 502ms
PostcssFilter | 465ms
but the CSS is the same as if it were output from SASS, not Post CSS.
Here is my angular-cli-build.js:
'use strict';
/* global require, module */
const Angular2App = require('angular-cli/lib/broccoli/angular2-app');
const compileSass = require('broccoli-sass');
const compileCSS = require('broccoli-postcss');
const cssnext = require('postcss-cssnext');
const cssnano = require('cssnano');
const mergeTrees = require('broccoli-merge-trees');
const Funnel = require('broccoli-funnel');
const _ = require('lodash');
const glob = require('glob');
var options = {
plugins: [
{
module: cssnext,
options: {
browsers: ['> 1%'],
warnForDuplicates: false
}
},
{
module: cssnano,
options: {
safe: true,
sourcemap: true
}
}
]
};
module.exports = function(defaults) {
let sourceDir = 'src';
let appTree = new Angular2App(defaults, {
sourceDir: sourceDir,
sassCompiler: {
includePaths: [
'src/style'
]
},
vendorNpmFiles: [
'systemjs/dist/system-polyfills.js',
'systemjs/dist/system.src.js',
'zone.js/dist/*.js',
'es6-shim/es6-shim.js',
'reflect-metadata/*.js',
'reflect-metadata/*.js.map',
'rxjs/**/*.js',
'#angular/**/*.js',
'rxjs/**/*.js.map',
'#angular/**/*.js.map',
'd3/d3.js',
'three/build/three.js',
'three/examples/js/postprocessing/*.js',
'three/examples/js/shaders/*.js'
]
});
let sass = mergeTrees(_.map(glob.sync('src/**/*.scss'), function(sassFile) {
sassFile = sassFile.replace('src/', '');
return compileSass(['src'], sassFile, sassFile.replace(/.scss$/, '.css'));
}));
let css = compileCSS(sass, options);
return mergeTrees([sass, css, appTree], { overwrite: true });
};
It was the order of operations, my trees were not overwriting appTree!
This works!
return mergeTrees([appTree, sass, css], { overwrite: true });

How to remap a Durandal viewmodel

This is my main.js file in a Durandal project.
What I'm trying to do is set things up so that the name 'upload-item' resolves to either 'upload-item' or 'upload-item-prehtml5' depending on whether File is defined.
requirejs.config({
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/bootstrap/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1.min',
'jquery-ui': '../lib/jquery-ui/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min',
'moment': '../lib/moment/moment',
'knockout-jqueryui': '../lib/knockout/knockout-jqueryui.min',
'file-size-formatting': '../lib/wone/file-size-formatting'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
define(['durandal/system', 'durandal/app', 'durandal/viewLocator'], function (system, app, viewLocator) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
var filetype = typeof(File);
if (filetype == 'undefined') {
//apply pre-html5 fixups
require.config({
map: {
'*': {
'upload-item': 'upload-item-prehtml5'
}
}
});
}
app.title = 'Jumbo File Transfer';
//specify which plugins to install and their configuration
app.configurePlugins({
router: true,
dialog: true,
widget: {
kinds: ['expander']
}
});
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();
//Show the app by setting the root view model for our application.
app.setRoot('shell');
});
});
Testing on IE8 shows that the call to require.config occurs and the mapping is added, but it doesn't seem to have the effect I expected: upload-item.js and upload-item.html are loaded when I expected upload-item-prehtml5.js and upload-item-prehtml5.html to be loaded.
If this is the wrong way to go about this, then what is the right way to perform this kind of conditional resolution?
It's not quite what I originally wanted, but I found you can do this:
var prehtml5 = (typeof (File) == 'undefined');
requirejs.config({
paths: {
...
'upload-item': prehtml5 ? 'upload-item-prehtml5' : 'upload-item'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
Path remapping seems to extend into the file name. Normally you wouldn't list siblings of main.js but you can and if you do then you can remap them, including the file name.