Grunt variable as filename and file path - node.js

I'm trying to use Grunt option to define a file path and file name. This used to work but now I'm getting an unexpected token error.
var myTarget = grunt.option('target');
'build/assets/css/' + myTarget + '.css': 'source/scss/' + myTarget + '/styles.scss'

You should use the special placeholders for variables in file names and strings. First, you should load the option (using grunt.option()) or configuration (using grunt.congif()), as an option for the Grunt initConfig method. Then you should use the special placeholders <%= varname %> to use the loaded options or configurations.
grunt.initConfig({
target : grunt.option('target'),
...
files : {
'build/assets/css/<%= target %>.css' : 'source/scss/<%= target %>/styles.scss'
}
...
});
Configuration can also load an object, so the special placeholder can match object properties too:
grunt.config('definitions', (function() {
return {
target : 'my-target'
};
})());
Later on your config:
grunt.initConfig({
config : grunt.config('definitions'),
...
files : {
'build/assets/css/<%= config.target %>.css' : 'source/scss/<%= config.target %>/styles.scss'
}
...
});
Read more about Grunt option, config and templates in the Grunt official website.
Hope it helps.

Related

How can I replace text from a config.json file automatically by a variable in node js

Hi and thank you for your help
I have a config.json file that contains this:
{
"test": {
"hi": {
"text": "Hi ${user.name}"
}
}
}
and I have index.js file that contains:
var config = require('./config.json')
var user = {name: "Test", tag: "#1234")
console.log(`${config.test.hi.text}`) // Output: "Hi ${user.name}"
// Expected output: Hi Test
I want when you change in the config.json the user.name to something like user.tag its automatically replaces him without .replace() function
thank you for your help :D
When using Template literals, the expressions in the placeholders and the text between the backticks (` `) get passed to a function that concatenates the strings into a single string, replacing the values inside $(variable).
This process happens at the time you define the template and cannot be resolved later as you do in your code. Refer to the documentation: Template literals
It would be also a bad coding practise as if the user variable didn't exist in the index.js file it wouldn't give you a compile error, but a nasty runtime error.
The only way to do it is to have your template literal in reach of your variable scope, that means that the template literal can read the variable at the moment it's executed. If you want to have the user instance and the template in different files, you can use a callback function as this:
config.js
const callback = (user) => {
return `Hi ${user.name}`
}
const config = {
callback,
anotherConfig: {
hi: {
example: "This is another config"
}
}
}
export default config;
index.js
import config from './config.js';
const user = {name: "Test", tag: "#1234"};
console.log(config.callback(user))
Output
Hi Test

Conditional settings for Gulp plugins dependent on source file

The plugin gulp-pug allows to pass global variables to pug files via data property.
What if we don't need full data set in each .pug file? To implement conditional data injection, we need to access to current vinyl file instance inside pipe(this.gulpPlugins.pug({}) or at least to know the source file absolute path. Possible?
const dataSetForTopPage = {
foo: "alpha",
bar: "bravo"
};
const dataSetForAboutPage = {
baz: "charlie",
hoge: "delta"
};
gulp.src(sourceFileGlobsOrAbsolutePath)
.pipe(gulpPlugins.pug({
data: /*
if path is 'top.pug' -> 'dataSetForTopPage',
else if path is 'about.pug' -> 'dataSetForAboutPage'
else -> empty object*/
}))
.pipe(Gulp.dest("output"));
I am using gulp-intercept plugin. But how to synchronize it with gulpPlugins.pug?
gulp.src(sourceFileGlobsOrAbsolutePath)
.pipe(this.gulpPlugins.intercept(vinylFile => {
// I can compute conditional data set here
// but how to execute gulpPlugins.pug() here?
}))
// ...
It was just one example, but we will deal with same problem when need to conditional plugins options for other gulp plugins, too. E. g:
.pipe(gulpPlugins.htmlPrettify({
indent_char: " ",
indent_size: // if source file in 'admin/**' -> 2, else if in 'auth/**' -> 3 else 4
}))
You'll need to modify the stream manually - through2 is probably the most used package for this purpose. Once in the through2 callback, you can pass the stream to your gulp plugins (as long as their transform functions are exposed) and conditionally pass them options. For example, here is a task:
pugtest = () => {
const dataSet = {
'top.pug': {
foo: "alpha",
bar: "bravo"
},
'about.pug': {
foo: "charlie",
bar: "delta"
}
};
return gulp.src('src/**/*.pug')
.pipe(through2.obj((file, enc, next) =>
gulpPlugins.pug({
// Grab the filename, and set pug data to the value found in dataSet by that name
data: dataSet[file.basename] || {}
})._transform(file, enc, next)
))
.pipe(through2.obj((file, enc, next) => {
const options = {
indent_char: ' ',
indent_size: 4
};
if(file.relative.match(/admin\//)) {
options.indent_size = 2;
} else if(file.relative.match(/auth\//)) {
options.indent_size = 3;
}
file.contents = new Buffer.from(html.prettyPrint(String(file.contents), options), enc);
next(null, file);
}))
.pipe(gulp.dest('output'));
}
For the pug step, we call through2.obj and create the pug plugin, passing it data grabbed from our object literal, indexed by filename in this example. So now the data passed into the compiler comes from that object literal.
For the html step you mention, gulp-html-prettify doesn't expose its transform function, so we can't reach into it and pass the transform back to the stream. But in this case that's OK, if you look at the source it's just a wrapper to prettyPrint in the html package. That's quite literally all it is doing. So we can just rig up our step using through2 to do the same thing, but changing our options based on the vinyl file's relative path.
That's it! For a working example see this repo: https://github.com/joshdavenport/stack-overflow-61314141-gulp-pug-conditional

Dom_munger issue with Node 7.7.3 - Path must be a string

I'm trying to update an application to support Node -v 7.7.3. But when I am running the grunt task dom_munger as per below:
dom_munger:{
read: {
options: {
read:[
{selector:'script[data-concat!="false"]',attribute:'src',writeto:'appjs', isPath: true},
{selector:'link[rel="stylesheet"][data-concat!="false"]',attribute:'href',writeto:'appcss'}
]
},
src: 'app/index.html'
}
}
I receive error:
Warning: Path must be a string. Received [ 'app/index.html' ] Use --force to continue.
I wonder if there is a way to rewrite above grunt task or if there might be a good alternative to dom_munger. Any help would be appreciated.
Per the grunt-dom-munger Github:
When isPath is true, the extracted values are assumed to be file
references and their path is made relative to the Gruntfile.js rather
than the file they're read from.
Try removing the isPath property, or altering it to match the path from your Gruntfile to the index.html file.
Remove isPath: true, and make sure that path in src attribute relative to the Gruntfile.js rather than the file they're read from.
If needs make a replace in path:
dom_munger: {
replacePath: {
options: {
callback: function($, file){
var scripts = $('script[data-concat!="false"]');
// NOTE: path is made relative to the Gruntfile.js rather than the file they're read from
for(var i=0, s, il=scripts.length; i<il; i++){
s = scripts[i];
if(s.attribs.src){
s.attribs.src = s.attribs.src.replace('../', '');
}
}
}
},
src: 'temp/index.html'
},
read: {
options: {
read: [
{selector:'script[data-concat!="false"]',attribute:'src',writeto:'appjs'},
{selector:'link[rel="stylesheet"][data-concat!="false"]',attribute:'href',writeto:'appcss'}
]
},
src: 'temp/index.html'
}
}
Thanks you! But this only seems to work if the Grunt and Index are in the same folder structure. My structure looks like this:
- /app
-index.html
- gruntfile.js
And without the attribute 'isPath' the dom_munger will look for js files in the same directory as where the Gruntfile is places.

Folder copy including files in gulp

I'm trying to copy 2 folders onto a single build folder and for the second path I want to copy whole libs folder including libs folder itself to destination.
var paths = {
'standalone' : '../app-ui/assets/js',
'standalone_libs' : '../app-ui/libs',
'destination' : '../SomeFolder'
}
gulp.task('folder-copy', function() {
return gulp.src([paths.standalone_js + '/*', paths.standalone_libs + '/*']).pipe(gulp.dest(paths.destination));
});
Structure according to code
->SomeFolder
->app.js [ file from ../app-ui/assets/js ]
-> angular/angular.js [ file from ../app-ui/libs ]
-> lodash/lodash.js [ file from ../app-ui/libs ]
Actual Structure wanted
->SomeFolder
->app.js [ file from ../app-ui/assets/js ]
-> libs
-> angular/angular.js [ file from ../app-ui/libs ]
-> lodash/lodash.js [ file from ../app-ui/libs ]
You could specify the base in gulp.src :
return gulp.src(['some/path/app/*', 'libs/**/*'], {base: '.'})
.pipe(gulp.dest('build'));
This will copy all your files in libs, with the directory structure intact. But the directory structure will be preserved for app.js also...
I would just do two separate copy.
You can use merge-stream. There's a recipe in gulp repository. It would boil down to something like this :
var merge = require('merge-stream')
var libs= gulp.src('libs/**/*', {base: '.'})
.pipe(gulp.dest('build'));
var app = gulp.src('app.js')
.pipe(gulp.dest('build'));
return merge(libs, app);
Try removing the /* because this means the whole content inside the folder is what you want.
So for the libs, I would not add the /* like follows
return gulp.src([paths.standalone_js + '/*', paths.standalone_libs ]).pipe(gulp.dest(paths.destination));
What about changing this:
var paths = {
'standalone' : '../app-ui/assets/js',
'standalone_libs' : '../app-ui/libs',
'destination' : '../SomeFolder'
}
to this:
var paths = {
'standalone' : '../app-ui/assets/js',
'standalone_libs' : '../app-ui/(libs)',
'destination' : '../SomeFolder'
}

require js defining modules in html script tags

I am trying to define a module for the initual configuration options of my app inside an script tag in my html. but I get the following error:
Error: Module name "options" has not been loaded yet for context: _. Use require([]) http://requirejs.org/docs/errors.html#notloaded
here is the html:
<script src="Scripts/require.js" data-main="/Recruiter/temp-search/App/main"></script>
<script>
define('options',['jquery'],function($) {
return options = {
salesPhoneNumber : '#ConfigurationManager.AppSettings.Get("SalesPhoneNumber")',
saleInfoMessage : "To access Temp Search candidate details, please call our team on " + salesPhoneNumber,
subscriptionInfo : #Html.Raw(new JavaScriptSerializer().Serialize(Model.AccessInfo ?? null)),
questionProLink: src="#(Request.Url.Scheme)://www.questionpro.com/a/TakeSurvey?id=#(Model.IsRecCon ? AppSettings.SurveyRecConId : AppSettings.SurveyOthersId)&custom1=#Model.RecruiterEmail&custom2=#Model.RecruiterId",
surveyEnabled: '#AppSettings.FlexSurveyEnabled',
whatsNewUrl: '#AppSettings.UrlWhatsNew',
salesPhoneNumber:salesPhoneNumber,
showSaleInfo: '#ViewBag.ShowSaleInfo',
fileDownloadFailCookieName:'#AppSettings.FileDownloadFail',
urls: {
signInUrl: '#string.Format("https://{0}/recruiter/account/signIn", Url.RequestContext.HttpContext.Request.Url.Host)',
signInTempsHome: '/recruiter/temp-search/home',
signInTempsSearch: '/recruiter/temp-search/api/temps',
checkAvailabilityUrl: '/recruiter/temp-search/api/availability',
searchUrl: '/recruiter/temp-search/api/temps/search',
accesslimitUrl: '/recruiter/temp-search/api/ecruiters/accessinfo',
previewUrl: '/recruiter/temp-search/api/temps/preview'
},
elements: {
signInRegisterDialog: $("#signInRegisterDialog"),
noSubscriptionDialog: $("#noSubscriptionDialog"),
searchForm: $("#searchForm"),
searchKeywords: $("#Keywords"),
searchLocation: $("#Location"),
searchRadius: $("#Radius"),
searchSortBy: $("#sortBy"),
searchTemp: $("#Temporary"),
searchContract: $("#Contract"),
searchPayRateFrom: $("#PayRateFrom"),
searchPayRateTo: $("#PayRateTo"),
searchAvailability: $("#AvailabilityConfirmed"),
locationErrorMsg: $("#locationErrorMsg"),
checkAll: $(".checkAll"),
checkCandidate: $(".checkCandidate"),
availability: {
availabilityBtn: $("#availabilityBtn"),
availabilityDialog: $("#availabilityDialog"),
additionalInformation: $("#AdditionalInformation"),
jobPosition: $("#JobPosition"),
jobLocation: $("#JobLocation"),
payRate: $("#JobPayRateFrom"),
payRateTo: $("#JobPayRateTo"),
startOn: $("#StartOnDate"),
duration: $("#Duration"),
checkAvailabilityForm: $("#checkAvailabilityForm"),
availabilityLocation: $("#checkAvailabilityForm #JobLocation"),
candidateIds: $("#CandidateIds"),
tempJobId: $("#TempJobId"),
msgPanel: $("#msgPanel"),
msg: $(".msg"),
errorAvailability: $("#availabilityError"),
availabilityConfirmationDialog: $("#availabilityConfirmationDialog"),
infoBubbleMessage : $("#infoBubbleMessage"),
availabilityConfirmationMsg: $("#availabilityConfirmationDialog .msgDialog"),
downloadInfoLink : $("#downloadInfoLink")
},
preview: {
previewBtn: $('.previewBtn')
},
messagePanel: $("#messagePanel")
},
minWageRate : #Constants.Range.ApprenticeshipsPerHourMin,
authentication : #(Request.IsAuthenticated.ToString().ToLower()),
minDate: '#String.Format("{0:yyyy/MM/dd}", DateTime.Now)',
pageInfo: {
number: #Model.Results.PageNumber,
size: #Model.Results.PageSize,
resultsCount: #Model.TotalResultsCount
},
criteria : #Html.Raw(new JavaScriptSerializer().Serialize(Model.Criteria)),
remainingAccessLimit: #Model.AccessInfo.Remaining,
totalAccessLimit: #Model.AccessInfo.Limit,
availableCandidates: #Model.AvailableCandidates,
candidates: #Html.Raw(new JavaScriptSerializer().Serialize(Model.Results ?? Model.Results.ToJSON()))
};
})
</script>
The problem is not with the code you show in your question but with how you ask RequireJS to load your module. The error message you show happens when you do a require call of this form:
var foo = require('foo');
This kind of require call does not work unless foo is already loaded, and to ensure it is already loaded you can manually load it yourself, or you can have RequireJS do it for you. However, to get RequireJS to do it for you, you need to write your code in a certain way. If you want a module to use foo and you want to use the require above then, you should do:
define(function (require) {
var foo = require('foo');
...
});
Or if you need to use module and exports, the callback can be function (require, exports, module) {....
Also, you should perform the following operations in this order:
Load RequireJS.
Execute define('options', ....
Then and only then start loading your application.
This means removing data-main and using an explicit require call after define('options'.

Resources