sassdoc
Version:
Release the docs!
437 lines (337 loc) • 11.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _find = require('babel-runtime/core-js/array/find');
var _find2 = _interopRequireDefault(_find);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _utils = require('./utils');
var _errors = require('./errors');
var errors = _interopRequireWildcard(_errors);
var _events = require('events');
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _jsYaml = require('js-yaml');
var _jsYaml2 = _interopRequireDefault(_jsYaml);
var _sassConvert = require('sass-convert');
var _sassConvert2 = _interopRequireDefault(_sassConvert);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var Environment = function (_EventEmitter) {
(0, _inherits3.default)(Environment, _EventEmitter);
/**
* @param {Logger} logger
* @param {Boolean} strict
*/
function Environment(logger) {
var verbose = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var strict = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
(0, _classCallCheck3.default)(this, Environment);
var _this = (0, _possibleConstructorReturn3.default)(this, (Environment.__proto__ || (0, _getPrototypeOf2.default)(Environment)).call(this));
_this.logger = logger;
_this.verbose = verbose;
_this.strict = strict;
_this.on('error', function (error) {
var friendlyErrors = [errors.SassDocError, _sassConvert2.default.BinaryError, _sassConvert2.default.VersionError];
if ((0, _find2.default)(friendlyErrors, function (c) {
return error instanceof c;
})) {
logger.error(error.message);
} else {
if (_utils.is.error(error) && 'stack' in error) {
logger.error(error.stack);
} else {
logger.error(error);
}
}
});
if (strict) {
_this.on('warning', function (warning) {
return _this.emit('error', warning);
});
} else {
_this.on('warning', function (warning) {
return logger.warn(warning.message);
});
}
return _this;
}
/**
* @param {Object|String} config
*/
(0, _createClass3.default)(Environment, [{
key: 'load',
value: function load(config) {
if (!config) {
return this.loadDefaultFile();
}
if (_utils.is.string(config)) {
return this.loadFile(config);
}
if (_utils.is.plainObject(config)) {
return this.loadObject(config);
}
this.emit('error', new errors.SassDocError('Invalid `config` argument, expected string, object or undefined.'));
}
/**
* Merge given configuration object, excluding reserved keys.
*
* @param {Object} config
*/
}, {
key: 'loadObject',
value: function loadObject(config) {
var _this2 = this;
if (this.file) {
this.file = _path2.default.resolve(this.file);
this.dir = _path2.default.dirname(this.file);
}
(0, _keys2.default)(config).filter(function (key) {
return ['verbose', 'strict'].indexOf(key) === -1;
}).forEach(function (k) {
if (k in _this2) {
return _this2.emit('error', new Error('Reserved configuration key `' + k + '`.'));
}
_this2[k] = config[k];
});
}
/**
* Get the configuration object from given file.
*
* If the file is not found, emit a warning and fallback to default.
*
* The `dir` property will be the directory of the given file or the CWD
* if no file is given. The configuration paths should be relative to
* it.
*
* The given logger will be injected in the configuration object for
* further usage.
*
* @param {String} file
*/
}, {
key: 'loadFile',
value: function loadFile(file) {
this.file = file;
if (!this.tryLoadCurrentFile()) {
this.emit('warning', new errors.Warning('Config file `' + file + '` not found.'));
this.logger.warn('Falling back to `.sassdocrc`');
this.loadDefaultFile();
}
}
/**
* Try to load default `.sassdocrc` configuration file, or fallback
* to an empty object.
*/
}, {
key: 'loadDefaultFile',
value: function loadDefaultFile() {
this.file = '.sassdocrc';
this.tryLoadCurrentFile();
}
/**
* Post process the configuration to ensure `package` and `theme`
* have uniform values.
*
* The `package` key is ensured to be an object. If it's a string, it's
* required as JSON, relative to the configuration file directory.
*
* The `theme` key, if present and not already a function, will be
* resolved to the actual theme function.
*/
}, {
key: 'postProcess',
value: function postProcess() {
if (!this.dir) {
this.dir = process.cwd();
}
if (!this.dest) {
this.dest = 'sassdoc';
this.destCwd = true;
}
this.dest = this.resolve(this.dest, this.destCwd);
this.displayDest = _path2.default.relative(process.cwd(), this.dest);
if (!this.package) {
this.defaultPackage();
}
if ((0, _typeof3.default)(this.package) !== 'object') {
this.loadPackage();
}
if (typeof this.theme !== 'function') {
this.loadTheme();
}
}
/**
* Process `this.package`.
*/
}, {
key: 'loadPackage',
value: function loadPackage() {
var file = this.resolve(this.package);
this.package = this.tryParseFile(file);
if (this.package) {
return;
}
this.emit('warning', new errors.Warning('Package file `' + file + '` not found.'));
this.logger.warn('Falling back to `package.json`.');
this.defaultPackage();
}
/**
* Load `package.json`.
*/
}, {
key: 'defaultPackage',
value: function defaultPackage() {
var file = this.resolve('package.json');
this.package = this.tryParseFile(file);
if (this.package) {
return;
}
this.logger.warn('No package information.');
this.package = {};
}
/**
* Process `this.theme`.
*/
}, {
key: 'loadTheme',
value: function loadTheme() {
if (this.theme === undefined) {
return this.defaultTheme();
}
var hasSlash = this.theme.includes('/');
var isScoped = this.theme.startsWith('@') && hasSlash;
// We assume it's a full scoped package name.
if (isScoped) {
return this.tryTheme(this.theme);
}
// We assume it's a theme name shorthand.
if (!hasSlash) {
return this.tryTheme('sassdoc-theme-' + this.theme);
}
// We assume it's a path to a local theme.
var theme = this.resolve(this.theme, this.themeCwd);
this.themeName = this.theme;
this.displayTheme = _path2.default.relative(process.cwd(), theme);
return this.tryTheme(theme);
}
/**
* Try to load given theme module, or fallback to default theme.
*
* @param {String} module
*/
}, {
key: 'tryTheme',
value: function tryTheme(module) {
try {
require.resolve(module);
} catch (err) {
this.emit('warning', new errors.Warning('Theme `' + this.theme + '` not found.'));
this.logger.warn('Falling back to default theme.');
return this.defaultTheme();
}
this.theme = require(module);
var str = Object.prototype.toString;
if (typeof this.theme !== 'function') {
this.emit('error', new errors.SassDocError('Given theme is ' + str(this.theme) + ', expected ' + str(str) + '.' // eslint-disable-line comma-spacing
));
return this.defaultTheme();
}
if (this.theme.length !== 2) {
this.logger.warn('Given theme takes ' + this.theme.length + ' arguments, expected 2.');
}
}
/**
* Load `sassdoc-theme-default`.
*/
}, {
key: 'defaultTheme',
value: function defaultTheme() {
try {
require.resolve('sassdoc-theme-default');
} catch (err) {
this.emit('error', new errors.SassDocError('Holy shit, the default theme was not found!'));
}
this.theme = require('sassdoc-theme-default');
this.themeName = this.displayTheme = 'default';
}
/**
* Try to load `this.file`, and if not found, return `false`.
*
* @return {Boolean}
*/
}, {
key: 'tryLoadCurrentFile',
value: function tryLoadCurrentFile() {
var config = this.tryParseFile(this.file);
if (!config) {
return false;
}
this.load(config);
return true;
}
/**
* Try `this.parseFile` and return `false` if an `ENOENT` error
* is thrown.
*
* Other exceptions are passed to the `error` event.
*
* @param {String} file
* @return {*}
*/
}, {
key: 'tryParseFile',
value: function tryParseFile(file) {
try {
return this.parseFile(file);
} catch (e) {
if (e.code !== 'ENOENT') {
return this.emit('error', e);
}
}
return false;
}
/**
* Load YAML or JSON from given file.
*
* @param {String} file
* @return {*}
*/
}, {
key: 'parseFile',
value: function parseFile(file) {
return _jsYaml2.default.safeLoad(_fs2.default.readFileSync(file, 'utf-8'));
}
/**
* Resolve given file from `this.dir`.
*
* @param {String} file
* @param {Boolean} cwd - whether it's relative to CWD (like when
* defined in CLI).
* @return {String}
*/
}, {
key: 'resolve',
value: function resolve(file) {
var cwd = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
return _path2.default.resolve(cwd ? process.cwd() : this.dir, file);
}
}]);
return Environment;
}(_events.EventEmitter);
exports.default = Environment;