+++ /dev/null
-/* @flow */
-"use strict";
-const createStylelint = require("./createStylelint");
-const debug = require("debug")("stylelint:standalone");
-const FileCache = require("./utils/FileCache");
-const formatters /*: Object*/ = require("./formatters");
-const fs = require("fs");
-const globby /*: Function*/ = require("globby");
-const hash = require("./utils/hash");
-const ignore = require("ignore");
-const needlessDisables /*: Function*/ = require("./needlessDisables");
-const path = require("path");
-const pify = require("pify");
-const pkg = require("../package.json");
-
-const DEFAULT_IGNORE_FILENAME = ".stylelintignore";
-const FILE_NOT_FOUND_ERROR_CODE = "ENOENT";
-const ALWAYS_IGNORED_GLOBS = ["**/node_modules/**", "**/bower_components/**"];
-
-/*::type CssSyntaxErrorT = {
- column: number;
- file?: string;
- input: {
- column: number;
- file?: string;
- line: number;
- source: string;
- };
- line: number;
- message: string;
- name: string;
- reason: string;
- source: string;
-}*/
-
-module.exports = function(
- options /*: stylelint$standaloneOptions */
-) /*: Promise<stylelint$standaloneReturnValue>*/ {
- const cacheLocation = options.cacheLocation;
- const code = options.code;
- const codeFilename = options.codeFilename;
- const config = options.config;
- const configBasedir = options.configBasedir;
- const configFile = options.configFile;
- const configOverrides = options.configOverrides;
- const customSyntax = options.customSyntax;
- const files = options.files;
- const fix = options.fix;
- const formatter = options.formatter;
- const ignoreDisables = options.ignoreDisables;
- const reportNeedlessDisables = options.reportNeedlessDisables;
- const syntax = options.syntax;
- const useCache = options.cache || false;
- let fileCache;
- const startTime = Date.now();
-
- // The ignorer will be used to filter file paths after the glob is checked,
- // before any files are actually read
- const ignoreFilePath = options.ignorePath || DEFAULT_IGNORE_FILENAME;
- const absoluteIgnoreFilePath = path.isAbsolute(ignoreFilePath)
- ? ignoreFilePath
- : path.resolve(process.cwd(), ignoreFilePath);
- let ignoreText = "";
- try {
- ignoreText = fs.readFileSync(absoluteIgnoreFilePath, "utf8");
- } catch (readError) {
- if (readError.code !== FILE_NOT_FOUND_ERROR_CODE) throw readError;
- }
- const ignorePattern = options.ignorePattern || [];
- const ignorer = ignore()
- .add(ignoreText)
- .add(ignorePattern);
-
- const isValidCode = typeof code === "string";
- if ((!files && !isValidCode) || (files && (code || isValidCode))) {
- throw new Error(
- "You must pass stylelint a `files` glob or a `code` string, though not both"
- );
- }
-
- let formatterFunction;
- if (typeof formatter === "string") {
- formatterFunction = formatters[formatter];
- if (formatterFunction === undefined) {
- return Promise.reject(
- new Error(
- "You must use a valid formatter option: 'json', 'string', 'verbose', or a function"
- )
- );
- }
- } else if (typeof formatter === "function") {
- formatterFunction = formatter;
- } else {
- formatterFunction = formatters.json;
- }
-
- const stylelint = createStylelint({
- config,
- configFile,
- configBasedir,
- configOverrides,
- ignoreDisables,
- reportNeedlessDisables,
- syntax,
- customSyntax,
- fix
- });
-
- if (!files) {
- const absoluteCodeFilename =
- codeFilename !== undefined && !path.isAbsolute(codeFilename)
- ? path.join(process.cwd(), codeFilename)
- : codeFilename;
- return stylelint
- ._lintSource({
- code,
- codeFilename: absoluteCodeFilename
- })
- .then(postcssResult => {
- // Check for file existence
- return new Promise((resolve, reject) => {
- if (!absoluteCodeFilename) {
- reject();
- return;
- }
-
- fs.stat(absoluteCodeFilename, err => {
- if (err) {
- reject();
- } else {
- resolve();
- }
- });
- })
- .then(() => {
- return stylelint._createStylelintResult(
- postcssResult,
- absoluteCodeFilename
- );
- })
- .catch(() => {
- return stylelint._createStylelintResult(postcssResult);
- });
- })
- .catch(handleError)
- .then(stylelintResult => {
- const returnValue = prepareReturnValue([stylelintResult]);
- const postcssResult = stylelintResult._postcssResult;
- // if file is ignored, return nothing
- if (
- absoluteCodeFilename &&
- !ignorer.filter(path.relative(process.cwd(), absoluteCodeFilename))
- .length
- ) {
- returnValue.output = "";
- } else if (
- options.fix &&
- postcssResult &&
- !postcssResult.stylelint.ignored
- ) {
- // If we're fixing, the output should be the fixed code
- returnValue.output = postcssResult.root.toString(
- postcssResult.opts.syntax
- );
- }
- return returnValue;
- });
- }
-
- let fileList = files;
- if (typeof fileList === "string") {
- fileList = [fileList];
- }
- if (!options.disableDefaultIgnores) {
- fileList = fileList.concat(ALWAYS_IGNORED_GLOBS.map(glob => "!" + glob));
- }
-
- if (useCache) {
- const stylelintVersion = pkg.version;
- const hashOfConfig = hash(`${stylelintVersion}_${JSON.stringify(config)}`);
- fileCache = new FileCache(cacheLocation, hashOfConfig);
- } else {
- // No need to calculate hash here, we just want to delete cache file.
- fileCache = new FileCache(cacheLocation);
- // Remove cache file if cache option is disabled
- fileCache.destroy();
- }
-
- return globby(fileList)
- .then(filePaths => {
- // The ignorer filter needs to check paths relative to cwd
- filePaths = ignorer.filter(
- filePaths.map(p => path.relative(process.cwd(), p))
- );
-
- if (!filePaths.length) {
- return Promise.all([]);
- }
-
- let absoluteFilePaths = filePaths.map(filePath => {
- const absoluteFilepath = !path.isAbsolute(filePath)
- ? path.join(process.cwd(), filePath)
- : path.normalize(filePath);
- return absoluteFilepath;
- });
-
- if (useCache) {
- absoluteFilePaths = absoluteFilePaths.filter(
- fileCache.hasFileChanged.bind(fileCache)
- );
- }
-
- const getStylelintResults = absoluteFilePaths.map(absoluteFilepath => {
- debug(`Processing ${absoluteFilepath}`);
- return stylelint
- ._lintSource({
- filePath: absoluteFilepath
- })
- .then(postcssResult => {
- if (postcssResult.stylelint.stylelintError && useCache) {
- debug(
- `${
- absoluteFilepath
- } contains linting errors and will not be cached.`
- );
- fileCache.removeEntry(absoluteFilepath);
- }
-
- // If we're fixing, save the file with changed code
- let fixFile = Promise.resolve();
- if (!postcssResult.stylelint.ignored && options.fix) {
- const fixedCss = postcssResult.root.toString(
- postcssResult.opts.syntax
- );
- fixFile = pify(fs.writeFile)(absoluteFilepath, fixedCss);
- }
-
- return fixFile.then(() =>
- stylelint._createStylelintResult(postcssResult, absoluteFilepath)
- );
- })
- .catch(handleError);
- });
-
- return Promise.all(getStylelintResults);
- })
- .then(stylelintResults => {
- if (useCache) {
- fileCache.reconcile();
- }
- return prepareReturnValue(stylelintResults);
- });
-
- function prepareReturnValue(
- stylelintResults /*: Array<stylelint$result>*/
- ) /*: stylelint$standaloneReturnValue*/ {
- const errored = stylelintResults.some(
- result => result.errored || result.parseErrors.length > 0
- );
- const returnValue /*: stylelint$standaloneReturnValue*/ = {
- errored,
- output: formatterFunction(stylelintResults),
- results: stylelintResults
- };
- if (reportNeedlessDisables) {
- returnValue.needlessDisables = needlessDisables(stylelintResults);
- }
- debug(`Linting complete in ${Date.now() - startTime}ms`);
- return returnValue;
- }
-};
-
-function handleError(error /*: Object*/) {
- if (error.name === "CssSyntaxError") {
- return convertCssSyntaxErrorToResult(error);
- } else {
- throw error;
- }
-}
-
-// By converting syntax errors to stylelint results,
-// we can control their appearance in the formatted output
-// and other tools like editor plugins can decide how to
-// present them, as well
-function convertCssSyntaxErrorToResult(
- error /*: CssSyntaxErrorT*/
-) /*: stylelint$result*/ {
- if (error.name !== "CssSyntaxError") {
- throw error;
- }
-
- return {
- source: error.file || "<input css 1>",
- deprecations: [],
- invalidOptionWarnings: [],
- parseErrors: [],
- errored: true,
- warnings: [
- {
- line: error.line,
- column: error.column,
- rule: error.name,
- severity: "error",
- text: error.reason + " (" + error.name + ")"
- }
- ]
- };
-}