UNPKG

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