UNPKG

4.09 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 // TODO: investigate why lazy import HTML syntax causes
82 // JS files with the word "html" to throw TypeError
83 // https://github.com/stylelint/stylelint/issues/4793
84 const { html, ...rest } = syntaxes;
85
86 syntax = autoSyntax({
87 css: cssSyntax(stylelint),
88 jsx: syntaxes['css-in-js'],
89 ...rest,
90 });
91 }
92
93 const postcssOptions = {
94 from: options.filePath,
95 syntax,
96 };
97
98 const source = options.code ? options.codeFilename : options.filePath;
99 let preProcessedCode = code;
100
101 if (options.codeProcessors && options.codeProcessors.length) {
102 if (stylelint._options.fix) {
103 // eslint-disable-next-line no-console
104 console.warn(
105 'Autofix is incompatible with processors and will be disabled. Are you sure you need a processor?',
106 );
107 stylelint._options.fix = false;
108 }
109
110 options.codeProcessors.forEach((codeProcessor) => {
111 preProcessedCode = codeProcessor(preProcessedCode, source);
112 });
113 }
114
115 const result = new LazyResult(postcssProcessor, preProcessedCode, postcssOptions);
116
117 return result;
118 })
119 .then((postcssResult) => {
120 if (options.filePath) {
121 stylelint._postcssResultCache.set(options.filePath, postcssResult);
122 }
123
124 return postcssResult;
125 });
126};
127
128/**
129 * @param {string} filePath
130 * @returns {Promise<string>}
131 */
132function readFile(filePath) {
133 return new Promise((resolve, reject) => {
134 fs.readFile(filePath, 'utf8', (err, content) => {
135 if (err) {
136 return reject(err);
137 }
138
139 resolve(content);
140 });
141 });
142}
143
144/**
145 * @param {StylelintInternalApi} stylelint
146 * @returns {Syntax}
147 */
148function cssSyntax(stylelint) {
149 return {
150 parse: stylelint._options.fix ? require('postcss-safe-parser') : postcss.parse,
151 stringify: postcss.stringify,
152 };
153}