UNPKG

sassdoc

Version:
437 lines (337 loc) 11.3 kB
'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;