diff --git a/package.json b/package.json index 340eba2..62ff1d4 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "lodash": "^4.17.14", "postcss": "^7.0.17", "postcss-icss-selectors": "^2.0.3", + "postcss-load-config": "^2.1.0", "reserved-words": "^0.1.2", "sass": "^1.22.4" }, diff --git a/src/@types/postcss-load.config.d.ts b/src/@types/postcss-load.config.d.ts new file mode 100644 index 0000000..38c5b00 --- /dev/null +++ b/src/@types/postcss-load.config.d.ts @@ -0,0 +1,15 @@ +declare module 'postcss-load-config' { + interface PostCSSConfig { + plugins: any[]; + options?: any; + } + + interface Load { + (context: object, path?: string, options?: object): Promise; + sync(context: object, path?: string, options?: object): PostCSSConfig; + } + + const load: Load; + + export = load; +} diff --git a/src/helpers/DtsSnapshotCreator.ts b/src/helpers/DtsSnapshotCreator.ts index 32bfe69..dcc7471 100644 --- a/src/helpers/DtsSnapshotCreator.ts +++ b/src/helpers/DtsSnapshotCreator.ts @@ -1,6 +1,5 @@ import { extractICSS, IICSSExports } from 'icss-utils'; import * as postcss from 'postcss'; -import * as postcssIcssSelectors from 'postcss-icss-selectors'; import * as ts_module from 'typescript/lib/tsserverlibrary'; import * as less from 'less'; import * as sass from 'sass'; @@ -10,7 +9,6 @@ import { Options } from '../options'; import { Logger } from './Logger'; const NOT_CAMELCASE_REGEXP = /[\-_]/; -const processor = postcss(postcssIcssSelectors({ mode: 'local' })); const classNameToProperty = (className: string) => `'${className}': string;`; const classNameToNamedExport = (className: string) => @@ -39,7 +37,7 @@ const getFilePath = (fileName: string) => export class DtsSnapshotCreator { constructor(private readonly logger: Logger) {} - getClasses(css: string, fileName: string) { + getClasses(processor: postcss.Processor, css: string, fileName: string) { try { const fileType = getFileType(fileName); let transformedCss = ''; @@ -62,7 +60,9 @@ export class DtsSnapshotCreator { const processedCss = processor.process(transformedCss); - return extractICSS(processedCss.root).icssExports; + return processedCss.root + ? extractICSS(processedCss.root).icssExports + : {}; } catch (e) { this.logger.error(e); return {}; @@ -97,12 +97,13 @@ export default classes; getDtsSnapshot( ts: typeof ts_module, + processor: postcss.Processor, fileName: string, scriptSnapshot: ts.IScriptSnapshot, options: Options, ) { const css = scriptSnapshot.getText(0, scriptSnapshot.getLength()); - const classes = this.getClasses(css, fileName); + const classes = this.getClasses(processor, css, fileName); const dts = this.createExports(classes, options); return ts.ScriptSnapshot.fromString(dts); } diff --git a/src/helpers/__tests__/DtsSnapshotCreator.test.ts b/src/helpers/__tests__/DtsSnapshotCreator.test.ts index 647e5a9..5bbff78 100644 --- a/src/helpers/__tests__/DtsSnapshotCreator.test.ts +++ b/src/helpers/__tests__/DtsSnapshotCreator.test.ts @@ -1,6 +1,8 @@ import { readFileSync } from 'fs'; import { IICSSExports } from 'icss-utils'; import { join } from 'path'; +import * as postcss from 'postcss'; +import * as postcssIcssSelectors from 'postcss-icss-selectors'; import { DtsSnapshotCreator } from '../DtsSnapshotCreator'; const testFileNames = [ @@ -11,6 +13,8 @@ const testFileNames = [ 'empty.module.scss', ]; +const processor = postcss([postcssIcssSelectors({ mode: 'local' })]); + describe('utils / cssSnapshots', () => { testFileNames.forEach((fileName) => { let classes: IICSSExports; @@ -23,7 +27,11 @@ describe('utils / cssSnapshots', () => { log: jest.fn(), error: jest.fn(), }); - classes = dtsSnapshotCreator.getClasses(testFile, fullFileName); + classes = dtsSnapshotCreator.getClasses( + processor, + testFile, + fullFileName, + ); }); describe(`with file '${fileName}'`, () => { diff --git a/src/index.ts b/src/index.ts index b119e26..22a5a65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,50 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as loadPostCssConfig from 'postcss-load-config'; import * as ts_module from 'typescript/lib/tsserverlibrary'; + import { createMatchers } from './helpers/createMatchers'; import { isCSSFn } from './helpers/cssExtensions'; import { DtsSnapshotCreator } from './helpers/DtsSnapshotCreator'; import { Options } from './options'; import { LanguageServiceLogger } from './helpers/Logger'; +import * as postcss from 'postcss'; +import * as postcssIcssSelectors from 'postcss-icss-selectors'; + +const removePlugin = postcss.plugin('remove-mixins', () => (css) => { + css.walkRules((rule) => { + rule.walkAtRules((atRule) => { + if (atRule.name === 'mixin') { + atRule.remove(); + } + }); + }); +}); + +function getPostCssConfig(dir: string) { + try { + return loadPostCssConfig.sync({}, dir); + } catch (error) { + return { plugins: [] }; + } +} + function init({ typescript: ts }: { typescript: typeof ts_module }) { let _isCSS: isCSSFn; + function create(info: ts.server.PluginCreateInfo) { const logger = new LanguageServiceLogger(info); const dtsSnapshotCreator = new DtsSnapshotCreator(logger); + const postcssConfig = getPostCssConfig(info.project.getCurrentDirectory()); + const processor = postcss([ + removePlugin(), + ...postcssConfig.plugins.filter( + // Postcss mixins plugin might be async and will break the postcss sync output. + (plugin) => !['postcss-mixins'].includes(plugin.postcssPlugin), + ), + postcssIcssSelectors({ mode: 'local' }), + ]); // User options for plugin. const options: Options = info.config.options || {}; @@ -32,6 +65,7 @@ function init({ typescript: ts }: { typescript: typeof ts_module }) { if (isCSS(fileName)) { scriptSnapshot = dtsSnapshotCreator.getDtsSnapshot( ts, + processor, fileName, scriptSnapshot, options, @@ -58,6 +92,7 @@ function init({ typescript: ts }: { typescript: typeof ts_module }) { if (isCSS(sourceFile.fileName)) { scriptSnapshot = dtsSnapshotCreator.getDtsSnapshot( ts, + processor, sourceFile.fileName, scriptSnapshot, options, diff --git a/yarn.lock b/yarn.lock index 4197c4b..569186b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -943,7 +943,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^5.2.0: +cosmiconfig@^5.0.0, cosmiconfig@^5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== @@ -1698,6 +1698,13 @@ image-size@~0.5.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -1706,6 +1713,13 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -3240,6 +3254,14 @@ postcss-icss-selectors@^2.0.3: lodash "^4.17.4" postcss "^6.0.2" +postcss-load-config@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" + integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + postcss@^6.0.2: version "6.0.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"