Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

externalizes core grunt methods to modules #1344

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
*.DS_Store
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to keep platform agnostic use a global .gitignore, see https://help.github.com/articles/ignoring-files/#create-a-global-gitignore

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did I create a new gitignore?! lol that was an accident. thanks for the link though! I'll remove it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) thanks for working on this stuff by the way, looks great!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh nvm I see what you mean, yeah no problem I'll revert that commit.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

node_modules
.npm-debug.log
tmp
21 changes: 17 additions & 4 deletions lib/grunt.js
Expand Up @@ -11,6 +11,7 @@

// Nodejs libs.
var path = require('path');
var logger = require('grunt-legacy-log-facade');

// This allows grunt to require() .coffee files.
require('coffee-script');
Expand All @@ -31,17 +32,29 @@ var Log = require('grunt-legacy-log').Log;
var log = new Log({grunt: grunt});
grunt.log = log;

gRequire('template');
gRequire('event');
var fail = gRequire('fail');
gRequire('file');
var option = gRequire('option');
var config = gRequire('config');
gRequire('event');
var file = gRequire('file');
var fail = gRequire('fail');
var template = gRequire('template');
var task = gRequire('task');
var help = gRequire('help');
gRequire('cli');

var verbose = grunt.verbose = log.verbose;

// setup logging event listeners
logger.logEventsToMethods(log, [
config.log.event,
fail.log.event,
file.log.event,
template.log.event,
]);
logger.logEventsToMethods(fail, [
file.fail.event,
]);

// Expose some grunt metadata.
grunt.package = require('../package.json');
grunt.version = grunt.package.version;
Expand Down
121 changes: 5 additions & 116 deletions lib/grunt/cli.js
Expand Up @@ -9,122 +9,11 @@

'use strict';

var cli = require('grunt-legacy-cli');
var grunt = require('../grunt');

// Nodejs libs.
var path = require('path');

// External libs.
var nopt = require('nopt');

// This is only executed when run via command line.
var cli = module.exports = function(options, done) {
// CLI-parsed options override any passed-in "default" options.
if (options) {
// For each default option...
Object.keys(options).forEach(function(key) {
if (!(key in cli.options)) {
// If this option doesn't exist in the parsed cli.options, add it in.
cli.options[key] = options[key];
} else if (cli.optlist[key].type === Array) {
// If this option's type is Array, append it to any existing array
// (or create a new array).
[].push.apply(cli.options[key], options[key]);
}
});
}

// Run tasks.
grunt.tasks(cli.tasks, cli.options, done);
};

// Default options.
var optlist = cli.optlist = {
help: {
short: 'h',
info: 'Display this help text.',
type: Boolean
},
base: {
info: 'Specify an alternate base path. By default, all file paths are relative to the Gruntfile. ' +
'(grunt.file.setBase) *',
type: path
},
color: {
info: 'Disable colored output.',
type: Boolean,
negate: true
},
gruntfile: {
info: 'Specify an alternate Gruntfile. By default, grunt looks in the current or parent directories ' +
'for the nearest Gruntfile.js or Gruntfile.coffee file.',
type: path
},
debug: {
short: 'd',
info: 'Enable debugging mode for tasks that support it.',
type: [Number, Boolean]
},
stack: {
info: 'Print a stack trace when exiting with a warning or fatal error.',
type: Boolean
},
force: {
short: 'f',
info: 'A way to force your way past warnings. Want a suggestion? Don\'t use this option, fix your code.',
type: Boolean
},
tasks: {
info: 'Additional directory paths to scan for task and "extra" files. (grunt.loadTasks) *',
type: Array
},
npm: {
info: 'Npm-installed grunt plugins to scan for task and "extra" files. (grunt.loadNpmTasks) *',
type: Array
},
write: {
info: 'Disable writing files (dry run).',
type: Boolean,
negate: true
},
verbose: {
short: 'v',
info: 'Verbose mode. A lot more information output.',
type: Boolean
},
version: {
short: 'V',
info: 'Print the grunt version. Combine with --verbose for more info.',
type: Boolean
},
// Even though shell auto-completion is now handled by grunt-cli, leave this
// option here for display in the --help screen.
completion: {
info: 'Output shell auto-completion rules. See the grunt-cli documentation for more information.',
type: String
},
};

// Parse `optlist` into a form that nopt can handle.
var aliases = {};
var known = {};

Object.keys(optlist).forEach(function(key) {
var short = optlist[key].short;
if (short) {
aliases[short] = '--' + key;
}
known[key] = optlist[key].type;
});

var parsed = nopt(known, aliases, process.argv, 2);
cli.tasks = parsed.argv.remain;
cli.options = parsed;
delete parsed.argv;
/**
* Expose `cli`
*/

// Initialize any Array options that weren't initialized.
Object.keys(optlist).forEach(function(key) {
if (optlist[key].type === Array && !(key in cli.options)) {
cli.options[key] = [];
}
});
module.exports = cli.create({grunt: grunt});
115 changes: 5 additions & 110 deletions lib/grunt/config.js
Expand Up @@ -9,116 +9,11 @@

'use strict';

var config = require('grunt-legacy-config');
var grunt = require('../grunt');

// Get/set config data. If value was passed, set. Otherwise, get.
var config = module.exports = function(prop, value) {
if (arguments.length === 2) {
// Two arguments were passed, set the property's value.
return config.set(prop, value);
} else {
// Get the property's value (or the entire data object).
return config.get(prop);
}
};

// The actual config data.
config.data = {};

// Escape any . in name with \. so dot-based namespacing works properly.
config.escape = function(str) {
return str.replace(/\./g, '\\.');
};

// Return prop as a string.
config.getPropString = function(prop) {
return Array.isArray(prop) ? prop.map(config.escape).join('.') : prop;
};

// Get raw, unprocessed config data.
config.getRaw = function(prop) {
if (prop) {
// Prop was passed, get that specific property's value.
return grunt.util.namespace.get(config.data, config.getPropString(prop));
} else {
// No prop was passed, return the entire config.data object.
return config.data;
}
};

// Match '<%= FOO %>' where FOO is a propString, eg. foo or foo.bar but not
// a method call like foo() or foo.bar().
var propStringTmplRe = /^<%=\s*([a-z0-9_$]+(?:\.[a-z0-9_$]+)*)\s*%>$/i;

// Get config data, recursively processing templates.
config.get = function(prop) {
return config.process(config.getRaw(prop));
};

// Expand a config value recursively. Used for post-processing raw values
// already retrieved from the config.
config.process = function(raw) {
return grunt.util.recurse(raw, function(value) {
// If the value is not a string, return it.
if (typeof value !== 'string') { return value; }
// If possible, access the specified property via config.get, in case it
// doesn't refer to a string, but instead refers to an object or array.
var matches = value.match(propStringTmplRe);
var result;
if (matches) {
result = config.get(matches[1]);
// If the result retrieved from the config data wasn't null or undefined,
// return it.
if (result != null) { return result; }
}
// Process the string as a template.
return grunt.template.process(value, {data: config.data});
});
};

// Set config data.
config.set = function(prop, value) {
return grunt.util.namespace.set(config.data, config.getPropString(prop), value);
};

// Deep merge config data.
config.merge = function(obj) {
grunt.util._.merge(config.data, obj);
return config.data;
};

// Initialize config data.
config.init = function(obj) {
grunt.verbose.write('Initializing config...').ok();
// Initialize and return data.
return (config.data = obj || {});
};
/**
* Expose `config`
*/

// Test to see if required config params have been defined. If not, throw an
// exception (use this inside of a task).
config.requires = function() {
var p = grunt.util.pluralize;
var props = grunt.util.toArray(arguments).map(config.getPropString);
var msg = 'Verifying propert' + p(props.length, 'y/ies') +
' ' + grunt.log.wordlist(props) + ' exist' + p(props.length, 's') +
' in config...';
grunt.verbose.write(msg);
var failProps = config.data && props.filter(function(prop) {
return config.get(prop) == null;
}).map(function(prop) {
return '"' + prop + '"';
});
if (config.data && failProps.length === 0) {
grunt.verbose.ok();
return true;
} else {
grunt.verbose.or.write(msg);
grunt.log.error().error('Unable to process task.');
if (!config.data) {
throw grunt.util.error('Unable to load config.');
} else {
throw grunt.util.error('Required config propert' +
p(failProps.length, 'y/ies') + ' ' + failProps.join(', ') + ' missing.');
}
}
};
module.exports = config.create(grunt.option);
75 changes: 5 additions & 70 deletions lib/grunt/fail.js
Expand Up @@ -9,76 +9,11 @@

'use strict';

var Fail = require('grunt-legacy-fail').Fail;
var grunt = require('../grunt');

// The module to be exported.
var fail = module.exports = {};

// Error codes.
fail.code = {
FATAL_ERROR: 1,
MISSING_GRUNTFILE: 2,
TASK_FAILURE: 3,
TEMPLATE_ERROR: 4,
INVALID_AUTOCOMPLETE: 5,
WARNING: 6,
};

// DRY it up!
function writeln(e, mode) {
grunt.log.muted = false;
var msg = String(e.message || e);
if (!grunt.option('no-color')) { msg += '\x07'; } // Beep!
if (mode === 'warn') {
msg = 'Warning: ' + msg + ' ';
msg += (grunt.option('force') ? 'Used --force, continuing.'.underline : 'Use --force to continue.');
msg = msg.yellow;
} else {
msg = ('Fatal error: ' + msg).red;
}
grunt.log.writeln(msg);
}

// If --stack is enabled, log the appropriate error stack (if it exists).
function dumpStack(e) {
if (grunt.option('stack')) {
if (e.origError && e.origError.stack) {
console.log(e.origError.stack);
} else if (e.stack) {
console.log(e.stack);
}
}
}

// A fatal error occurred. Abort immediately.
fail.fatal = function(e, errcode) {
writeln(e, 'fatal');
dumpStack(e);
grunt.util.exit(typeof errcode === 'number' ? errcode : fail.code.FATAL_ERROR);
};

// Keep track of error and warning counts.
fail.errorcount = 0;
fail.warncount = 0;

// A warning occurred. Abort immediately unless -f or --force was used.
fail.warn = function(e, errcode) {
var message = typeof e === 'string' ? e : e.message;
fail.warncount++;
writeln(message, 'warn');
// If -f or --force aren't used, stop script processing.
if (!grunt.option('force')) {
dumpStack(e);
grunt.log.writeln().fail('Aborted due to warnings.');
grunt.util.exit(typeof errcode === 'number' ? errcode : fail.code.WARNING);
}
};
/**
* Expose `fail`
*/

// This gets called at the very end.
fail.report = function() {
if (fail.warncount > 0) {
grunt.log.writeln().fail('Done, but with warnings.');
} else {
grunt.log.writeln().success('Done, without errors.');
}
};
module.exports = new Fail(grunt.option);