import { restoreAll, spyOn } from 'nanospy' import { test } from 'uvu' import { equal, is, match, throws, type } from 'uvu/assert' import postcss = require('../lib/postcss.js') import postcssDefault, { PluginCreator, Root } from '../lib/postcss.js' import Processor from '../lib/processor.js' test.after.each(() => { restoreAll() }) test('default matches module.exports', () => { is(postcss, postcssDefault) }) test('creates plugins list', () => { let processor = postcss() is(processor instanceof Processor, true) equal(processor.plugins, []) }) test('saves plugins list', () => { let a = (): void => {} let b = (): void => {} equal(postcss(a, b).plugins, [a, b]) }) test('saves plugins list as array', () => { let a = (): void => {} let b = (): void => {} equal(postcss([a, b]).plugins, [a, b]) }) test('takes plugin from other processor', () => { let a = (): void => {} let b = (): void => {} let c = (): void => {} let other = postcss([a, b]) equal(postcss([other, c]).plugins, [a, b, c]) }) test('takes plugins from a a plugin returning a processor', () => { let a = (): void => {} let b = (): void => {} let c = (): void => {} let other = postcss([a, b]) let meta = (() => other) as PluginCreator meta.postcss = true equal(postcss([other, c]).plugins, [a, b, c]) }) test('contains parser', () => { is(postcss.parse('').type, 'root') }) test('contains stringifier', () => { type(postcss.stringify, 'function') }) test('allows to build own CSS', () => { let root = postcss.root({ raws: { after: '\n' } }) let comment = postcss.comment({ text: 'Example' }) let media = postcss.atRule({ name: 'media', params: 'screen' }) let rule = postcss.rule({ selector: 'a' }) let decl = postcss.decl({ prop: 'color', value: 'black' }) root.append(comment) rule.append(decl) media.append(rule) root.append(media) is( root.toString(), '/* Example */\n' + '@media screen {\n' + ' a {\n' + ' color: black\n' + ' }\n' + '}\n' ) }) test('allows to build own CSS with Document', () => { let document = postcss.document() let root = postcss.root({ raws: { after: '\n' } }) let comment = postcss.comment({ text: 'Example' }) let media = postcss.atRule({ name: 'media', params: 'screen' }) let rule = postcss.rule({ selector: 'a' }) let decl = postcss.decl({ prop: 'color', value: 'black' }) root.append(comment) rule.append(decl) media.append(rule) root.append(media) document.append(root) is( document.toString(), '/* Example */\n' + '@media screen {\n' + ' a {\n' + ' color: black\n' + ' }\n' + '}\n' ) }) test('contains list module', () => { equal(postcss.list.space('a b'), ['a', 'b']) }) test('works with null', () => { throws(() => { // @ts-expect-error Testing invalid input postcss([() => {}]).process(null).css }, /PostCSS received null instead of CSS string/) }) test('has deprecated method to create plugins', () => { let warn = spyOn(console, 'warn', () => {}) let plugin = (postcss as any).plugin('test', (filter?: string) => { return (root: Root) => { root.walkDecls(filter ?? 'two', i => { i.remove() }) } }) equal(warn.callCount, 0) let func1: any = postcss(plugin).plugins[0] is(func1.postcssPlugin, 'test') match(func1.postcssVersion, /\d+.\d+.\d+/) equal(warn.callCount, 1) let func2: any = postcss(plugin()).plugins[0] equal(func2.postcssPlugin, func1.postcssPlugin) equal(func2.postcssVersion, func1.postcssVersion) let result1 = postcss(plugin('one')).process('a{ one: 1; two: 2 }') is(result1.css, 'a{ two: 2 }') let result2 = postcss(plugin).process('a{ one: 1; two: 2 }') is(result2.css, 'a{ one: 1 }') equal(warn.callCount, 1) match(warn.calls[0][0], /postcss\.plugin was deprecated/) }) test('creates a shortcut to process css', async () => { let warn = spyOn(console, 'warn', () => {}) let plugin = (postcss as any).plugin('test', (str?: string) => { return (root: Root) => { root.walkDecls(i => { i.value = str ?? 'bar' }) } }) let result1 = plugin.process('a{value:foo}') is(result1.css, 'a{value:bar}') let result2 = plugin.process('a{value:foo}', {}, 'baz') is(result2.css, 'a{value:baz}') let result = await plugin.process('a{value:foo}', { from: 'a' }, 'baz') equal(result.opts, { from: 'a' }) is(result.css, 'a{value:baz}') equal(warn.callCount, 1) }) test('does not call plugin constructor', () => { let warn = spyOn(console, 'warn', () => {}) let calls = 0 let plugin = (postcss as any).plugin('test', () => { calls += 1 return () => {} }) is(calls, 0) postcss(plugin).process('a{}') is(calls, 1) postcss(plugin()).process('a{}') is(calls, 2) equal(warn.callCount, 1) }) test.run()