UNPKG

4.28 kBJavaScriptView Raw
1'use strict';
2
3const dynamicRequire = require('./dynamicRequire');
4const fs = require('fs');
5const LazyResult = require('postcss/lib/lazy-result');
6const postcss = require('postcss');
7/** @type {import('postcss-syntax')} */
8let autoSyntax = null;
9
10/** @typedef {import('stylelint').StylelintInternalApi} StylelintInternalApi */
11/** @typedef {{parse: any, stringify: any}} Syntax */
12
13const postcssProcessor = postcss();
14
15/**
16 * @param {StylelintInternalApi} stylelint
17 * @param {import('stylelint').GetPostcssOptions} options
18 *
19 * @returns {Promise<import('postcss').Result>}
20 */
21module.exports = function(stylelint, options = {}) {
22 const cached = options.filePath ? stylelint._postcssResultCache.get(options.filePath) : undefined;
23
24 if (cached) return Promise.resolve(cached);
25
26 /** @type {Promise<string> | undefined} */
27 let getCode;
28
29 if (options.code !== undefined) {
30 getCode = Promise.resolve(options.code);
31 } else if (options.filePath) {
32 getCode = readFile(options.filePath);
33 }
34
35 if (!getCode) {
36 throw new Error('code or filePath required');
37 }
38
39 return getCode
40 .then((code) => {
41 const customSyntax = stylelint._options.customSyntax;
42 /** @type {Syntax | null} */
43 let syntax = null;
44
45 if (customSyntax) {
46 try {
47 // TODO TYPES determine which type has customSyntax
48 const useCustomSyntax = /** @type {any} */ dynamicRequire(customSyntax);
49
50 /*
51 * PostCSS allows for syntaxes that only contain a parser, however,
52 * it then expects the syntax to be set as the `parser` option rather than `syntax`.
53 */
54 if (!useCustomSyntax.parse) {
55 syntax = {
56 parse: useCustomSyntax,
57 stringify: postcss.stringify,
58 };
59 } else {
60 syntax = useCustomSyntax;
61 }
62 } catch (e) {
63 throw new Error(`Cannot resolve custom syntax module ${customSyntax}`);
64 }
65 } else if (stylelint._options.syntax === 'css') {
66 syntax = cssSyntax(stylelint);
67 } else if (stylelint._options.syntax) {
68 /** @type {{[k: string]: string}} */
69 const syntaxes = {
70 'css-in-js': 'postcss-jsx',
71 html: 'postcss-html',
72 less: 'postcss-less',
73 markdown: 'postcss-markdown',
74 sass: 'postcss-sass',
75 scss: 'postcss-scss',
76 sugarss: 'sugarss',
77 };
78
79 const syntaxFromOptions = syntaxes[stylelint._options.syntax];
80
81 if (!syntaxFromOptions) {
82 throw new Error(
83 'You must use a valid syntax option, either: css, css-in-js, html, less, markdown, sass, scss, or sugarss',
84 );
85 }
86
87 syntax = dynamicRequire(syntaxFromOptions);
88 } else if (
89 !(options.codeProcessors && options.codeProcessors.length) ||
90 (options.filePath && /\.(scss|sass|less)$/.test(options.filePath))
91 ) {
92 if (!autoSyntax) {
93 autoSyntax = require('postcss-syntax');
94 }
95
96 syntax = autoSyntax({
97 css: cssSyntax(stylelint),
98 });
99 }
100
101 const postcssOptions = {
102 from: options.filePath,
103 syntax,
104 };
105
106 const source = options.code ? options.codeFilename : options.filePath;
107 let preProcessedCode = code;
108
109 if (options.codeProcessors && options.codeProcessors.length) {
110 if (stylelint._options.fix) {
111 // eslint-disable-next-line no-console
112 console.warn(
113 'Autofix is incompatible with processors and will be disabled. Are you sure you need a processor?',
114 );
115 stylelint._options.fix = false;
116 }
117
118 options.codeProcessors.forEach((codeProcessor) => {
119 preProcessedCode = codeProcessor(preProcessedCode, source);
120 });
121 }
122
123 const result = new LazyResult(postcssProcessor, preProcessedCode, postcssOptions);
124
125 return result;
126 })
127 .then((postcssResult) => {
128 if (options.filePath) {
129 stylelint._postcssResultCache.set(options.filePath, postcssResult);
130 }
131
132 return postcssResult;
133 });
134};
135
136/**
137 * @param {string} filePath
138 * @returns {Promise<string>}
139 */
140function readFile(filePath) {
141 return new Promise((resolve, reject) => {
142 fs.readFile(filePath, 'utf8', (err, content) => {
143 if (err) {
144 return reject(err);
145 }
146
147 resolve(content);
148 });
149 });
150}
151
152/**
153 * @param {StylelintInternalApi} stylelint
154 * @returns {Syntax}
155 */
156function cssSyntax(stylelint) {
157 return {
158 parse: stylelint._options.fix ? dynamicRequire('postcss-safe-parser') : postcss.parse,
159 stringify: postcss.stringify,
160 };
161}