diff --git a/plugin-packs/postcss-preset-env/.tape.js b/plugin-packs/postcss-preset-env/.tape.js index ace2906744..d7a04f40a6 100644 --- a/plugin-packs/postcss-preset-env/.tape.js +++ b/plugin-packs/postcss-preset-env/.tape.js @@ -235,6 +235,27 @@ module.exports = { stage: 0 } }, + 'import:ch88': { + message: 'supports { browsers: "chrome >= 88", importFrom: { customMedia, customProperties, customSelectors, environmentVariables } } usage', + options: { + browsers: 'chrome >= 88', + importFrom: { + customMedia: { + '--narrow-window': '(max-width: env(--sm))' + }, + customProperties: { + '--order': '1' + }, + customSelectors: { + ':--heading': 'h1, h2, h3, h4, h5, h6' + }, + environmentVariables: { + '--sm': '40rem' + } + }, + stage: 0 + } + }, 'basic:export': { message: 'supports { stage: 0 } usage', options: { diff --git a/plugin-packs/postcss-preset-env/test/import.ch88.expect.css b/plugin-packs/postcss-preset-env/test/import.ch88.expect.css new file mode 100644 index 0000000000..1ec7a1ea9d --- /dev/null +++ b/plugin-packs/postcss-preset-env/test/import.ch88.expect.css @@ -0,0 +1,14 @@ +.test-custom-properties { + order: var(--order); +} + +@media (max-width: 40rem) { + .test-custom-media-queries { + order: 2; + } +} + +h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.test-custom-selectors,h5.test-custom-selectors,h6.test-custom-selectors { + order: 3; + } + diff --git a/plugins/postcss-custom-properties/.tape.js b/plugins/postcss-custom-properties/.tape.js index c5593174f7..f69475d26f 100644 --- a/plugins/postcss-custom-properties/.tape.js +++ b/plugins/postcss-custom-properties/.tape.js @@ -1,4 +1,5 @@ const importWithProps = require('./test/import'); +const assert = require('assert').strict; module.exports = { 'basic': { @@ -80,6 +81,17 @@ module.exports = { expect: 'basic.import.expect.css', result: 'basic.import.result.css' }, + 'basic:import-cjs': { + message: 'supports { importFrom: "test/import-properties{-2}?.cjs" } usage', + options: { + importFrom: [ + 'test/import-properties.cjs', + 'test/import-properties-2.cjs' + ] + }, + expect: 'basic.import.expect.css', + result: 'basic.import.result.css' + }, 'basic:import-css': { message: 'supports { importFrom: "test/import-properties{-2}?.css" } usage', options: { @@ -153,6 +165,25 @@ module.exports = { expect: 'basic.import-override.expect.css', result: 'basic.import-override.result.css' }, + 'basic:import-override:inverse': { + message: 'importFrom with { preserve: false, overrideImportFromWithRoot: true } should override importFrom properties', + options: { + preserve: false, + overrideImportFromWithRoot: true, + importFrom: { + customProperties: { + '--color': 'rgb(0, 0, 0)', + '--color-2': 'yellow', + '--ref-color': 'var(--color)', + '--margin': '0 10px 20px 30px', + '--shadow-color': 'rgb(0,0,0)', + '--z-index': 10 + } + } + }, + expect: 'basic.import-override.inverse.expect.css', + result: 'basic.import-override.inverse.result.css' + }, 'basic:export': { message: 'supports { exportTo: { customProperties: { ... } } } usage', options: { @@ -207,9 +238,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.scss', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.scss', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.scss', 'utf8')); } }, 'basic:export-json': { @@ -223,9 +252,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.json', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.json', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.json', 'utf8')); } }, 'basic:export-js': { @@ -239,9 +266,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.js', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.js', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.js', 'utf8')); } }, 'basic:export-mjs': { @@ -255,9 +280,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.mjs', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.mjs', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.mjs', 'utf8')); } }, 'basic:export-css': { @@ -271,9 +294,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.css', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.css', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.css', 'utf8')); } }, 'basic:export-css-to': { @@ -287,9 +308,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.css', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.css', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.css', 'utf8')); } }, 'basic:export-css-to-type': { @@ -303,9 +322,7 @@ module.exports = { global.__exportPropertiesString = require('fs').readFileSync('test/export-properties.css', 'utf8'); }, after() { - if (global.__exportPropertiesString !== require('fs').readFileSync('test/export-properties.css', 'utf8')) { - throw new Error('The original file did not match the freshly exported copy'); - } + assert.strictEqual(global.__exportPropertiesString, require('fs').readFileSync('test/export-properties.css', 'utf8')); } }, 'basic:import-is-empty': { diff --git a/plugins/postcss-custom-properties/CHANGELOG.md b/plugins/postcss-custom-properties/CHANGELOG.md index 6448b28fb0..026b65f0ac 100755 --- a/plugins/postcss-custom-properties/CHANGELOG.md +++ b/plugins/postcss-custom-properties/CHANGELOG.md @@ -1,5 +1,13 @@ # Changes to PostCSS Custom Properties +### Unreleased + +- Add `overrideImportFromWithRoot` option +- Allow `.mjs` in `importFrom` +- Converted to typescript +- Correct typings for plugin options +- Fix unicode support in custom property names + ### 12.0.4 (January 7, 2022) - Fixed an issue that was causing synchronous mode to not being able to pick and transform properties that were added as part of the PostCSS flow. ([#132](https://github.com/csstools/postcss-plugins/issues/132)) diff --git a/plugins/postcss-custom-properties/README.md b/plugins/postcss-custom-properties/README.md index b87ff7e258..b0ff2167d7 100755 --- a/plugins/postcss-custom-properties/README.md +++ b/plugins/postcss-custom-properties/README.md @@ -137,6 +137,17 @@ postcssCustomProperties({ See example imports written in [CSS](test/import-properties.css), [JS](test/import-properties.js), and [JSON](test/import-properties.json). +### overrideImportFromWithRoot + +The `overrideImportFromWithRoot` option determines if properties added via `importFrom` are overridden by properties that exist in `:root`. +Defaults to `false`. + +```js +postcssCustomProperties({ + overrideImportFromWithRoot: true +}); +``` + ### exportTo The `exportTo` option specifies destinations where Custom Properties can be exported diff --git a/plugins/postcss-custom-properties/index.d.ts b/plugins/postcss-custom-properties/index.d.ts deleted file mode 100644 index af2437344b..0000000000 --- a/plugins/postcss-custom-properties/index.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type * as PostCSS from 'postcss' - -type ImportExportObject = { customProperties: {} } -type ImportExportFunction = (customProperties?: {}) => ImportExportObject -type ImportExportFilepath = string - -type ImportExport = ImportExportFilepath | ImportExportObject | ImportExportFunction | (ImportExportFilepath | ImportExportObject | ImportExportFunction)[] - -export interface PluginOptions { - /** Determines whether Custom Properties and properties using custom properties should be preserved in their original form. */ - preserve?: boolean - - /** Specifies sources where Custom Properties can be imported from, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ - importFrom?: ImportExport - - /** Specifies destinations where Custom Properties can be exported to, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ - exportTo?: ImportExport -} - -export interface Plugin { - (options?: PluginOptions): { - postcssPlugin: 'postcss-custom-properties', - prepare({ root }: { root: any }): ( - | { - Declaration: (decl: any) => void; - Once?: undefined; - } - | { - Once: (root: any) => Promise; - Declaration: (decl: any) => void; - } - ) - }, - postcss: true -} - -declare const plugin: Plugin - -export default plugin diff --git a/plugins/postcss-custom-properties/package.json b/plugins/postcss-custom-properties/package.json index 47ece39f62..e1ec1edf63 100644 --- a/plugins/postcss-custom-properties/package.json +++ b/plugins/postcss-custom-properties/package.json @@ -11,7 +11,14 @@ "bugs": "https://github.com/csstools/postcss-plugins/issues", "main": "dist/index.cjs", "module": "dist/index.mjs", - "types": "index.d.ts", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs", + "default": "./dist/index.mjs" + } + }, "files": [ "CHANGELOG.md", "LICENSE.md", diff --git a/plugins/postcss-custom-properties/src/index.js b/plugins/postcss-custom-properties/src/index.js deleted file mode 100755 index cc750fc91d..0000000000 --- a/plugins/postcss-custom-properties/src/index.js +++ /dev/null @@ -1,54 +0,0 @@ -import getCustomPropertiesFromRoot from './lib/get-custom-properties-from-root'; -import getCustomPropertiesFromImports from './lib/get-custom-properties-from-imports'; -import transformProperties from './lib/transform-properties'; -import writeCustomPropertiesToExports from './lib/write-custom-properties-to-exports'; - -const creator = opts => { - // whether to preserve custom selectors and rules using them - const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : true; - - // sources to import custom selectors from - const importFrom = [].concat(Object(opts).importFrom || []); - - // destinations to export custom selectors to - const exportTo = [].concat(Object(opts).exportTo || []); - - // promise any custom selectors are imported - const customPropertiesPromise = getCustomPropertiesFromImports(importFrom); - - let customProperties; - - // whether to return synchronous function if no asynchronous operations are requested - const canReturnSyncFunction = importFrom.length === 0 && exportTo.length === 0; - - return { - postcssPlugin: 'postcss-custom-properties', - prepare () { - if (canReturnSyncFunction) { - return { - Once: (root) => { - customProperties = getCustomPropertiesFromRoot(root, { preserve }); - }, - Declaration: (decl) => transformProperties(decl, customProperties, { preserve }), - }; - } else { - return { - Once: async root => { - customProperties = Object.assign( - {}, - getCustomPropertiesFromRoot(root, { preserve }), - await customPropertiesPromise, - ); - - await writeCustomPropertiesToExports(customProperties, exportTo); - }, - Declaration: (decl) => transformProperties(decl, customProperties, { preserve }), - }; - } - }, - }; -}; - -creator.postcss = true; - -export default creator; diff --git a/plugins/postcss-custom-properties/src/index.ts b/plugins/postcss-custom-properties/src/index.ts new file mode 100755 index 0000000000..3ca5c3e8c7 --- /dev/null +++ b/plugins/postcss-custom-properties/src/index.ts @@ -0,0 +1,95 @@ +import type { PluginCreator } from 'postcss'; +import type valuesParser from 'postcss-value-parser'; + +import getCustomPropertiesFromRoot from './lib/get-custom-properties-from-root'; +import getCustomPropertiesFromImports from './lib/get-custom-properties-from-imports'; +import transformProperties from './lib/transform-properties'; +import writeCustomPropertiesToExports from './lib/write-custom-properties-to-exports'; +import type { ImportOptions, ExportOptions } from './lib/options'; + +export interface PluginOptions { + /** Determines whether Custom Properties and properties using custom properties should be preserved in their original form. */ + preserve?: boolean + + /** Specifies sources where Custom Properties can be imported from, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ + importFrom?: ImportOptions | Array + + /** Specifies destinations where Custom Properties can be exported to, which might be CSS, JS, and JSON files, functions, and directly passed objects. */ + exportTo?: ExportOptions | Array + + /** Specifies if `importFrom` properties or `:root` properties have priority. */ + overrideImportFromWithRoot?: boolean + +} + +const creator: PluginCreator = (opts?: PluginOptions) => { + // whether to preserve custom selectors and rules using them + const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : true; + const overrideImportFromWithRoot = 'overrideImportFromWithRoot' in Object(opts) ? Boolean(opts.overrideImportFromWithRoot) : false; + + // sources to import custom selectors from + let importFrom: Array = []; + if (Array.isArray(opts?.importFrom)) { + importFrom = opts.importFrom; + } else if (opts?.importFrom) { + importFrom = [opts.importFrom]; + } + + // destinations to export custom selectors to + let exportTo: Array = []; + if (Array.isArray(opts?.exportTo)) { + exportTo = opts.exportTo; + } else if (opts?.exportTo) { + exportTo = [opts.exportTo]; + } + + // promise any custom selectors are imported + const customPropertiesPromise = getCustomPropertiesFromImports(importFrom); + + let customProperties: Map = new Map(); + + // whether to return synchronous function if no asynchronous operations are requested + const canReturnSyncFunction = importFrom.length === 0 && exportTo.length === 0; + + return { + postcssPlugin: 'postcss-custom-properties', + prepare () { + if (canReturnSyncFunction) { + return { + Once: (root) => { + customProperties = getCustomPropertiesFromRoot(root, { preserve }); + }, + Declaration: (decl) => { + transformProperties(decl, customProperties, { preserve }); + }, + }; + } else { + return { + Once: async root => { + const importedCustomerProperties = (await customPropertiesPromise).entries(); + const rootCustomProperties = getCustomPropertiesFromRoot(root, { preserve: preserve }).entries(); + + if (overrideImportFromWithRoot) { + for (const [name, value] of [...importedCustomerProperties, ...rootCustomProperties]) { + customProperties.set(name, value); + } + } else { + for (const [name, value] of [...rootCustomProperties, ...importedCustomerProperties]) { + customProperties.set(name, value); + } + } + + await writeCustomPropertiesToExports(customProperties, exportTo); + }, + Declaration: (decl) => { + transformProperties(decl, customProperties, { preserve }); + }, + }; + } + }, + }; +}; + +creator.postcss = true; + +export default creator; diff --git a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-imports.js b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-imports.js deleted file mode 100644 index fa3656f584..0000000000 --- a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-imports.js +++ /dev/null @@ -1,110 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { parse } from 'postcss'; -import valuesParser from 'postcss-value-parser'; -import getCustomPropertiesFromRoot from './get-custom-properties-from-root'; - -/* Get Custom Properties from CSS File -/* ========================================================================== */ - -async function getCustomPropertiesFromCSSFile(from) { - const css = await readFile(from); - const root = parse(css, { from }); - - return getCustomPropertiesFromRoot(root, { preserve: true }); -} - -/* Get Custom Properties from Object -/* ========================================================================== */ - -function getCustomPropertiesFromObject(object) { - const customProperties = Object.assign( - {}, - Object(object).customProperties, - Object(object)['custom-properties'], - ); - - for (const key in customProperties) { - customProperties[key] = valuesParser(String(customProperties[key])); - } - - return customProperties; -} - -/* Get Custom Properties from JSON file -/* ========================================================================== */ - -async function getCustomPropertiesFromJSONFile(from) { - const object = await readJSON(from); - - return getCustomPropertiesFromObject(object); -} - -/* Get Custom Properties from JS file -/* ========================================================================== */ - -async function getCustomPropertiesFromJSFile(from) { - const object = await import(from); - - return getCustomPropertiesFromObject(object); -} - -/* Get Custom Properties from Imports -/* ========================================================================== */ - -export default function getCustomPropertiesFromImports(sources) { - return sources.map(source => { - if (source instanceof Promise) { - return source; - } else if (source instanceof Function) { - return source(); - } - - // read the source as an object - const opts = source === Object(source) ? source : { from: String(source) }; - - // skip objects with Custom Properties - if (opts.customProperties || opts['custom-properties']) { - return opts; - } - - // source pathname - const from = path.resolve(String(opts.from || '')); - - // type of file being read from - const type = (opts.type || path.extname(from).slice(1)).toLowerCase(); - - return { type, from }; - }).reduce(async (customProperties, source) => { - const { type, from } = await source; - - if (type === 'css' || type === 'pcss') { - return Object.assign(await customProperties, await getCustomPropertiesFromCSSFile(from)); - } - - if (type === 'js') { - return Object.assign(await customProperties, await getCustomPropertiesFromJSFile(from)); - } - - if (type === 'json') { - return Object.assign(await customProperties, await getCustomPropertiesFromJSONFile(from)); - } - - return Object.assign(await customProperties, await getCustomPropertiesFromObject(await source)); - }, {}); -} - -/* Helper utilities -/* ========================================================================== */ - -const readFile = from => new Promise((resolve, reject) => { - fs.readFile(from, 'utf8', (error, result) => { - if (error) { - reject(error); - } else { - resolve(result); - } - }); -}); - -const readJSON = async from => JSON.parse(await readFile(from)); diff --git a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-imports.ts b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-imports.ts new file mode 100644 index 0000000000..811baba312 --- /dev/null +++ b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-imports.ts @@ -0,0 +1,146 @@ +import getCustomPropertiesFromRoot from './get-custom-properties-from-root'; +import path from 'path'; +import type { ImportCustomProperties, ImportOptions } from './options'; +import valuesParser from 'postcss-value-parser'; +import { parse } from 'postcss'; +import { promises as fsp } from 'fs'; + +/* Get Custom Properties from CSS File +/* ========================================================================== */ + +async function getCustomPropertiesFromCSSFile(from) { + const css = await fsp.readFile(from); + const root = parse(css, { from : from.toString() }); + + return getCustomPropertiesFromRoot(root, { preserve: true }); +} + +/* Get Custom Properties from Object +/* ========================================================================== */ + +function getCustomPropertiesFromObject(object: ImportCustomProperties): Map { + const out: Map = new Map(); + + if ('customProperties' in object) { + for (const [name, value] of Object.entries(object.customProperties)) { + out.set(name, valuesParser(value.toString())); + } + } + + if ('custom-properties' in object) { + for (const [name, value] of Object.entries(object['custom-properties'])) { + out.set(name, valuesParser(value.toString())); + } + } + + return out; +} + +/* Get Custom Properties from JSON file +/* ========================================================================== */ + +async function getCustomPropertiesFromJSONFile(from): Promise> { + const object = await readJSON(from); + + return getCustomPropertiesFromObject(object); +} + +/* Get Custom Properties from JS file +/* ========================================================================== */ + +async function getCustomPropertiesFromJSFile(from): Promise> { + const object = await import(from); + + return getCustomPropertiesFromObject(object); +} + +/* Get Custom Properties from Imports +/* ========================================================================== */ + +export default async function getCustomPropertiesFromImports(sources: Array): Promise> { + const sourceData = (await Promise.all(sources.map(async (source) => { + if (source instanceof Promise) { + source = await source; + } else if (source instanceof Function) { + source = await source(); + } + + if (typeof source === 'string') { + const fromPath = path.resolve(source); + const type = path.extname(fromPath).slice(1).toLowerCase(); + + return { + type: type, + from: fromPath, + }; + } + + if ('customProperties' in source && Object(source.customProperties) === source.customProperties) { + return source; + } + + if ('custom-properties' in source && Object(source['custom-properties']) === source['custom-properties']) { + return source; + } + + if ('from' in source) { + const fromPath = path.resolve(source.from); + let type = source.type; + if (!type) { + type = path.extname(fromPath).slice(1).toLowerCase(); + } + return { + type: type, + from: fromPath, + }; + } + + if (Object.keys(source).length === 0) { + // Empty `importFrom` object. + return null; + } + + throw new Error(`Invalid source object: ${source}`); + }))).filter((x) => { + return !!x; + }); + + const data: Array> = await Promise.all(sourceData.map(async (partialData) => { + if (('type' in partialData) && ('from' in partialData)) { + if (partialData.type === 'css' || partialData.type === 'pcss') { + return await getCustomPropertiesFromCSSFile(partialData.from); + } + + if (partialData.type === 'js' || partialData.type === 'cjs') { + return await getCustomPropertiesFromJSFile(partialData.from); + } + + if (partialData.type === 'mjs') { + // Only works when running as a module. + return await getCustomPropertiesFromJSFile(partialData.from); + } + + if (partialData.type === 'json') { + return await getCustomPropertiesFromJSONFile(partialData.from); + } + + throw new Error('Invalid source type: ' + partialData.type); + } + + return getCustomPropertiesFromObject(partialData); + })); + + const out: Map = new Map(); + data.forEach((partialData) => { + for (const [name, value] of partialData.entries()) { + out.set(name, value); + } + }); + + return out; +} + +/* Helper utilities +/* ========================================================================== */ + +const readJSON = async from => JSON.parse((await fsp.readFile(from)).toString()); diff --git a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.js b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts similarity index 69% rename from plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.js rename to plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts index 7b81d6ae64..e177478fa1 100644 --- a/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.js +++ b/plugins/postcss-custom-properties/src/lib/get-custom-properties-from-root.ts @@ -2,10 +2,11 @@ import valuesParser from 'postcss-value-parser'; import { isBlockIgnored } from './is-ignored'; // return custom selectors from the css root, conditionally removing them -export default function getCustomPropertiesFromRoot(root, opts) { +export default function getCustomPropertiesFromRoot(root, opts): Map { // initialize custom selectors - const customPropertiesFromHtmlElement = {}; - const customPropertiesFromRootPseudo = {}; + const customPropertiesFromHtmlElement: Map = new Map(); + const customPropertiesFromRootPseudo: Map = new Map(); + const out: Map = new Map(); // for each html or :root rule root.nodes.slice().forEach(rule => { @@ -18,11 +19,11 @@ export default function getCustomPropertiesFromRoot(root, opts) { // for each custom property if (customPropertiesObject) { rule.nodes.slice().forEach(decl => { - if (isCustomDecl(decl) && !isBlockIgnored(decl)) { + if (decl.variable && !isBlockIgnored(decl)) { const { prop } = decl; // write the parsed value to the custom property - customPropertiesObject[prop] = valuesParser(decl.value); + customPropertiesObject.set(prop, valuesParser(decl.value)); // conditionally remove the custom property declaration if (!opts.preserve) { @@ -38,21 +39,25 @@ export default function getCustomPropertiesFromRoot(root, opts) { } }); + for (const [name, value] of customPropertiesFromHtmlElement.entries()) { + out.set(name, value); + } + + for (const [name, value] of customPropertiesFromRootPseudo.entries()) { + out.set(name, value); + } + // return all custom properties, preferring :root properties over html properties - return { ...customPropertiesFromHtmlElement, ...customPropertiesFromRootPseudo }; + return out; } // match html and :root rules const htmlSelectorRegExp = /^html$/i; const rootSelectorRegExp = /^:root$/i; -const customPropertyRegExp = /^--[A-z][\w-]*$/; // whether the node is an html or :root rule const isHtmlRule = node => node.type === 'rule' && node.selector.split(',').some(item => htmlSelectorRegExp.test(item)) && Object(node.nodes).length; const isRootRule = node => node.type === 'rule' && node.selector.split(',').some(item => rootSelectorRegExp.test(item)) && Object(node.nodes).length; -// whether the node is an custom property -const isCustomDecl = node => node.type === 'decl' && customPropertyRegExp.test(node.prop); - // whether the node is a parent without children const isEmptyParent = node => Object(node.nodes).length === 0; diff --git a/plugins/postcss-custom-properties/src/lib/is-ignored.js b/plugins/postcss-custom-properties/src/lib/is-ignored.ts similarity index 84% rename from plugins/postcss-custom-properties/src/lib/is-ignored.js rename to plugins/postcss-custom-properties/src/lib/is-ignored.ts index 3bcc7e7775..e1c5ccc5c0 100644 --- a/plugins/postcss-custom-properties/src/lib/is-ignored.js +++ b/plugins/postcss-custom-properties/src/lib/is-ignored.ts @@ -1,12 +1,12 @@ function isBlockIgnored(ruleOrDeclaration) { - var rule = ruleOrDeclaration.selector ? + const rule = ruleOrDeclaration.selector ? ruleOrDeclaration : ruleOrDeclaration.parent; return /(!\s*)?postcss-custom-properties:\s*off\b/i.test(rule.toString()); } function isRuleIgnored(rule) { - var previous = rule.prev(); + const previous = rule.prev(); return Boolean(isBlockIgnored(rule) || previous && diff --git a/plugins/postcss-custom-properties/src/lib/options.ts b/plugins/postcss-custom-properties/src/lib/options.ts new file mode 100644 index 0000000000..6db403f7ff --- /dev/null +++ b/plugins/postcss-custom-properties/src/lib/options.ts @@ -0,0 +1,14 @@ + +export type ImportFromSource = { from: string, type?: string } | string; +export type ImportCustomProperties = { customProperties?: Record, 'custom-properties'?: Record }; +export type ImportAsFunction = () => ImportFromSource | ImportCustomProperties +export type ImportAsPromise = Promise +export type ImportAsFunctionPromise = () => Promise +export type ImportOptions = ImportFromSource | ImportCustomProperties | ImportAsFunction | ImportAsPromise | ImportAsFunctionPromise; + +export type ExportJSONFunction = (customProperties?: Record) => Record; +export type ExportToSource = { to: string, type?: string, toJSON: ExportJSONFunction } | string; +export type ExportCustomProperties = { customProperties?: Record, 'custom-properties'?: Record, toJSON: ExportJSONFunction }; +export type ExportAsFunction = (ExportCustomProperties) => void +export type ExportAsFunctionPromise = (ExportCustomProperties) => Promise +export type ExportOptions = ExportToSource | ExportCustomProperties | ExportAsFunction | ExportAsFunctionPromise; diff --git a/plugins/postcss-custom-properties/src/lib/transform-properties.js b/plugins/postcss-custom-properties/src/lib/transform-properties.ts similarity index 100% rename from plugins/postcss-custom-properties/src/lib/transform-properties.js rename to plugins/postcss-custom-properties/src/lib/transform-properties.ts diff --git a/plugins/postcss-custom-properties/src/lib/transform-value-ast.js b/plugins/postcss-custom-properties/src/lib/transform-value-ast.ts similarity index 76% rename from plugins/postcss-custom-properties/src/lib/transform-value-ast.js rename to plugins/postcss-custom-properties/src/lib/transform-value-ast.ts index 0070bdb4ac..d14346492a 100644 --- a/plugins/postcss-custom-properties/src/lib/transform-value-ast.js +++ b/plugins/postcss-custom-properties/src/lib/transform-value-ast.ts @@ -6,12 +6,12 @@ export default function transformValueAST(root, customProperties) { const { value: name } = propertyNode; const index = root.nodes.indexOf(child); - if (name in Object(customProperties)) { + if (customProperties.has(name)) { // Direct match of a custom property to a parsed value - const nodes = customProperties[name].nodes; + const nodes = customProperties.get(name).nodes; // Re-transform nested properties without given one to avoid circular from keeping this forever - retransformValueAST({ nodes }, customProperties, name); + reTransformValueAST({ nodes }, customProperties, name); if (index > -1) { root.nodes.splice(index, 1, ...nodes); @@ -34,11 +34,11 @@ export default function transformValueAST(root, customProperties) { return root.toString(); } -// retransform the current ast without a custom property (to prevent recursion) -function retransformValueAST(root, customProperties, withoutProperty) { - const nextCustomProperties = Object.assign({}, customProperties); +// reTransform the current ast without a custom property (to prevent recursion) +function reTransformValueAST(root, customProperties, withoutProperty) { + const nextCustomProperties = new Map(customProperties); - delete nextCustomProperties[withoutProperty]; + nextCustomProperties.delete(withoutProperty); return transformValueAST(root, nextCustomProperties); } diff --git a/plugins/postcss-custom-properties/src/lib/write-custom-properties-to-exports.js b/plugins/postcss-custom-properties/src/lib/write-custom-properties-to-exports.ts similarity index 50% rename from plugins/postcss-custom-properties/src/lib/write-custom-properties-to-exports.js rename to plugins/postcss-custom-properties/src/lib/write-custom-properties-to-exports.ts index 58924cbbad..bbd0b1cb65 100644 --- a/plugins/postcss-custom-properties/src/lib/write-custom-properties-to-exports.js +++ b/plugins/postcss-custom-properties/src/lib/write-custom-properties-to-exports.ts @@ -1,5 +1,8 @@ -import fs from 'fs'; +import type valuesParser from 'postcss-value-parser'; + +import { promises as fsp } from 'fs'; import path from 'path'; +import type { ExportOptions } from './options'; /* Write Custom Properties to CSS File /* ========================================================================== */ @@ -12,7 +15,7 @@ async function writeCustomPropertiesToCssFile(to, customProperties) { }, []).join('\n'); const css = `:root {\n${cssContent}\n}\n`; - await writeFile(to, css); + await fsp.writeFile(to, css); } /* Write Custom Properties to SCSS File @@ -27,7 +30,7 @@ async function writeCustomPropertiesToScssFile(to, customProperties) { }, []).join('\n'); const scss = `${scssContent}\n`; - await writeFile(to, scss); + await fsp.writeFile(to, scss); } /* Write Custom Properties to JSON file @@ -39,7 +42,7 @@ async function writeCustomPropertiesToJsonFile(to, customProperties) { }, null, ' '); const json = `${jsonContent}\n`; - await writeFile(to, json); + await fsp.writeFile(to, json); } /* Write Custom Properties to Common JS file @@ -53,7 +56,7 @@ async function writeCustomPropertiesToCjsFile(to, customProperties) { }, []).join(',\n'); const js = `module.exports = {\n\tcustomProperties: {\n${jsContents}\n\t}\n};\n`; - await writeFile(to, js); + await fsp.writeFile(to, js); } /* Write Custom Properties to Module JS file @@ -67,83 +70,90 @@ async function writeCustomPropertiesToMjsFile(to, customProperties) { }, []).join(',\n'); const mjs = `export const customProperties = {\n${mjsContents}\n};\n`; - await writeFile(to, mjs); + await fsp.writeFile(to, mjs); } /* Write Custom Properties to Exports /* ========================================================================== */ -export default function writeCustomPropertiesToExports(customProperties, destinations) { +export default function writeCustomPropertiesToExports(customProperties, destinations: Array) { return Promise.all(destinations.map(async destination => { if (destination instanceof Function) { - await destination(defaultCustomPropertiesToJSON(customProperties)); + await destination(defaultCustomPropertiesToJSONObject(customProperties)); + return; + } + + if (typeof destination === 'string') { + const toPath = path.resolve(destination); + const type = path.extname(toPath).slice(1).toLowerCase(); + + await writePropertiesToFile(toPath, type, defaultCustomPropertiesToJSONObject(customProperties)); + return; + } + + // transformer for Custom Properties into a JSON-compatible object + let customPropertiesAsJSONObject = {}; + if ('toJSON' in destination) { + customPropertiesAsJSONObject = destination.toJSON(defaultCustomPropertiesToJSONObject(customProperties)); } else { - // read the destination as an object - const opts = destination === Object(destination) ? destination : { to: String(destination) }; - - // transformer for Custom Properties into a JSON-compatible object - const toJSON = opts.toJSON || defaultCustomPropertiesToJSON; - - if ('customProperties' in opts) { - // write directly to an object as customProperties - opts.customProperties = toJSON(customProperties); - } else if ('custom-properties' in opts) { - // write directly to an object as custom-properties - opts['custom-properties'] = toJSON(customProperties); - } else { - // destination pathname - const to = String(opts.to || ''); - - // type of file being written to - const type = (opts.type || path.extname(opts.to).slice(1)).toLowerCase(); - - // transformed Custom Properties - const customPropertiesJSON = toJSON(customProperties); - - if (type === 'css') { - await writeCustomPropertiesToCssFile(to, customPropertiesJSON); - } - - if (type === 'scss') { - await writeCustomPropertiesToScssFile(to, customPropertiesJSON); - } - - if (type === 'js') { - await writeCustomPropertiesToCjsFile(to, customPropertiesJSON); - } - - if (type === 'json') { - await writeCustomPropertiesToJsonFile(to, customPropertiesJSON); - } - - if (type === 'mjs') { - await writeCustomPropertiesToMjsFile(to, customPropertiesJSON); - } + customPropertiesAsJSONObject = defaultCustomPropertiesToJSONObject(customProperties); + } + + if ('to' in destination) { + const toPath = path.resolve(destination.to); + let type = destination.type; + if (!type) { + type = path.extname(toPath).slice(1).toLowerCase(); } + + await writePropertiesToFile(toPath, type, customPropertiesAsJSONObject); + return; + } + + if ('customProperties' in destination) { + // write directly to an object as customProperties + destination.customProperties = customPropertiesAsJSONObject; + } else if ('custom-properties' in destination) { + // write directly to an object as custom-properties + destination['custom-properties'] = customPropertiesAsJSONObject; } })); } +async function writePropertiesToFile(to: string, type: string, customProperties: Record) { + if (type === 'css') { + await writeCustomPropertiesToCssFile(to, customProperties); + } + + if (type === 'scss') { + await writeCustomPropertiesToScssFile(to, customProperties); + } + + if (type === 'js') { + await writeCustomPropertiesToCjsFile(to, customProperties); + } + + if (type === 'json') { + await writeCustomPropertiesToJsonFile(to, customProperties); + } + + if (type === 'mjs') { + await writeCustomPropertiesToMjsFile(to, customProperties); + } +} + /* Helper utilities /* ========================================================================== */ -const defaultCustomPropertiesToJSON = customProperties => { - return Object.keys(customProperties).reduce((customPropertiesJSON, key) => { - const valueNodes = customProperties[key]; - customPropertiesJSON[key] = valueNodes.toString(); +function defaultCustomPropertiesToJSONObject(customProperties: Map): Record { + const out = {}; + for (const [name, value] of customProperties.entries()) { + out[name] = value.toString(); + } - return customPropertiesJSON; - }, {}); -}; - -const writeFile = (to, text) => new Promise((resolve, reject) => { - fs.writeFile(to, text, error => { - if (error) { - reject(error); - } else { - resolve(); - } - }); -}); + return out; +} -const escapeForJS = string => string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r'); +const escapeForJS = (string) => { + return string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r'); +}; diff --git a/plugins/postcss-custom-properties/test/_import.mjs b/plugins/postcss-custom-properties/test/_import.mjs index 3ed159d010..98f8767664 100644 --- a/plugins/postcss-custom-properties/test/_import.mjs +++ b/plugins/postcss-custom-properties/test/_import.mjs @@ -1,6 +1,17 @@ import assert from 'assert'; import plugin from 'postcss-custom-properties'; +import postcss from 'postcss'; +import fs from 'fs'; + plugin(); assert.ok(plugin.postcss, 'should have "postcss flag"'); assert.equal(typeof plugin, 'function', 'should return a function'); + +postcss([plugin({ importFrom: 'test/import-properties.cjs' })]). + process(fs.readFileSync('test/basic.css', 'utf8'), {from: 'test/basic.css'}). + then(); + +postcss([plugin({ importFrom: 'test/import-properties.mjs' })]). + process(fs.readFileSync('test/basic.css', 'utf8'), {from: 'test/basic.css'}). + then(); diff --git a/plugins/postcss-custom-properties/test/_require.cjs b/plugins/postcss-custom-properties/test/_require.cjs index c86945dad4..a9a3a08975 100644 --- a/plugins/postcss-custom-properties/test/_require.cjs +++ b/plugins/postcss-custom-properties/test/_require.cjs @@ -1,6 +1,13 @@ const assert = require('assert'); const plugin = require('postcss-custom-properties'); +const postcss = require('postcss'); +const fs = require('fs'); + plugin(); assert.ok(plugin.postcss, 'should have "postcss flag"'); assert.equal(typeof plugin, 'function', 'should return a function'); + +postcss([plugin({ importFrom: 'test/import-properties.cjs' })]). + process(fs.readFileSync('test/basic.css', 'utf8'), {from: 'test/basic.css'}). + then(); diff --git a/plugins/postcss-custom-properties/test/basic.css b/plugins/postcss-custom-properties/test/basic.css index f6e3ac9c50..0c8deab3de 100644 --- a/plugins/postcss-custom-properties/test/basic.css +++ b/plugins/postcss-custom-properties/test/basic.css @@ -19,6 +19,7 @@ html { --url-2: url('/my/path'); --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; color: var(--color); } @@ -125,3 +126,11 @@ html { order: 4; background: var(--url-4); } + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: var(--✅-size); +} diff --git a/plugins/postcss-custom-properties/test/basic.expect.css b/plugins/postcss-custom-properties/test/basic.expect.css index 8532e05110..e7c5743ff0 100644 --- a/plugins/postcss-custom-properties/test/basic.expect.css +++ b/plugins/postcss-custom-properties/test/basic.expect.css @@ -19,6 +19,7 @@ html { --url-2: url('/my/path'); --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; color: rgb(255, 0, 0); color: var(--color); } @@ -144,3 +145,12 @@ html { background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); background: var(--url-4); } + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} diff --git a/plugins/postcss-custom-properties/test/basic.import-is-empty.expect.css b/plugins/postcss-custom-properties/test/basic.import-is-empty.expect.css index 8532e05110..e7c5743ff0 100644 --- a/plugins/postcss-custom-properties/test/basic.import-is-empty.expect.css +++ b/plugins/postcss-custom-properties/test/basic.import-is-empty.expect.css @@ -19,6 +19,7 @@ html { --url-2: url('/my/path'); --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; color: rgb(255, 0, 0); color: var(--color); } @@ -144,3 +145,12 @@ html { background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); background: var(--url-4); } + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} diff --git a/plugins/postcss-custom-properties/test/basic.import-override.expect.css b/plugins/postcss-custom-properties/test/basic.import-override.expect.css index 3cd943cfd7..e078da1ce4 100644 --- a/plugins/postcss-custom-properties/test/basic.import-override.expect.css +++ b/plugins/postcss-custom-properties/test/basic.import-override.expect.css @@ -97,3 +97,11 @@ order: 4; background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); } + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; +} diff --git a/plugins/postcss-custom-properties/test/basic.import-override.inverse.expect.css b/plugins/postcss-custom-properties/test/basic.import-override.inverse.expect.css new file mode 100644 index 0000000000..167bb66934 --- /dev/null +++ b/plugins/postcss-custom-properties/test/basic.import-override.inverse.expect.css @@ -0,0 +1,107 @@ +:root { + color: rgb(255, 0, 0); +} + +.ignore-line { + /* postcss-custom-properties: ignore next */ + color: var(--color); + background-color: yellow; +} + +.ignore-block { + /* postcss-custom-properties: off */ + color: var(--color-2, blue); + box-shadow: inset 0 -3px 0 var(--color); + background-image: linear-gradient(to right, var(--color, transparent) 0%, var(--color, transparent) 100%); +} + +.test { + --skip: gray; + color: rgb(255, 0, 0); +} + +.test--color_spacing { + box-shadow: inset 0 -3px 0 rgb(255, 0, 0); +} + +.test--preserve_whitespaces { + margin: 0 10px 20px 30px; +} + +.test--complex_values { + box-shadow: 0 6px 14px 0 color(rgb(255,0,0) a(.15)); +} + +.test--comma_separated_values { + font-family: "Open Sans", sans-serif; +} + +.test--fallback { + color: yellow; +} + +.test--color_w_var { + color: rgb(255, 0, 0); +} + +.test--color_w_vars { + color: hsl(0, 100%, 50%); +} + +.test--circular_var { + color: var(--circular); +} + +.test--z-index { + z-index: 10; +} + +.test--nested-fallback { + z-index: 1; +} + +.text--calc { + width: calc((100% - 1px) + 10px); +} + +.test--linear-gradient { + background-image: linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 100%); +} + +.test--loose-formatting { + color: rgb(255, 0, 0)/*rtl:red*/; +} + +.test--combined-selector { + color: #053; +} + +.test--variable-with-url { + order: 1; + background: url("/my/path"); +} + +.test--variable-with-url { + order: 2; + background: url('/my/path'); +} + + +.test--variable-with-url { + order: 3; + background: url(/my/path); +} + + +.test--variable-with-url { + order: 4; + background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); +} + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; +} diff --git a/plugins/postcss-custom-properties/test/basic.import.expect.css b/plugins/postcss-custom-properties/test/basic.import.expect.css index 4b31a16a65..c09bd2234c 100644 --- a/plugins/postcss-custom-properties/test/basic.import.expect.css +++ b/plugins/postcss-custom-properties/test/basic.import.expect.css @@ -19,6 +19,7 @@ html { --url-2: url('/my/path'); --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; color: rgb(255, 0, 0); color: var(--color); } @@ -145,3 +146,12 @@ html { background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); background: var(--url-4); } + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; + color: var(--✅-size); +} diff --git a/plugins/postcss-custom-properties/test/basic.preserve.expect.css b/plugins/postcss-custom-properties/test/basic.preserve.expect.css index 16bfadb7ae..a78e89b2e7 100644 --- a/plugins/postcss-custom-properties/test/basic.preserve.expect.css +++ b/plugins/postcss-custom-properties/test/basic.preserve.expect.css @@ -97,3 +97,11 @@ order: 4; background: url(data:image/png;bm90LWFuZC1pbWFnZQ==); } + +.no-prototype-collisions { + color: var(toString); +} + +.test-unicode { + color: 2em; +} diff --git a/plugins/postcss-custom-properties/test/export-properties.css b/plugins/postcss-custom-properties/test/export-properties.css index 57c2332ffc..6c5bc15ff7 100644 --- a/plugins/postcss-custom-properties/test/export-properties.css +++ b/plugins/postcss-custom-properties/test/export-properties.css @@ -15,5 +15,6 @@ --url-2: url('/my/path'); --url-3: url(/my/path); --url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); + --✅-size: 2em; --theme-color: #053; } diff --git a/plugins/postcss-custom-properties/test/export-properties.js b/plugins/postcss-custom-properties/test/export-properties.js index b863d35dcc..4a881c6cd8 100644 --- a/plugins/postcss-custom-properties/test/export-properties.js +++ b/plugins/postcss-custom-properties/test/export-properties.js @@ -16,6 +16,7 @@ module.exports = { '--url-2': 'url(\'/my/path\')', '--url-3': 'url(/my/path)', '--url-4': 'url(data:image/png;bm90LWFuZC1pbWFnZQ==)', + '--✅-size': '2em', '--theme-color': '#053' } }; diff --git a/plugins/postcss-custom-properties/test/export-properties.json b/plugins/postcss-custom-properties/test/export-properties.json index 93752ccf7b..723f042baf 100644 --- a/plugins/postcss-custom-properties/test/export-properties.json +++ b/plugins/postcss-custom-properties/test/export-properties.json @@ -16,6 +16,7 @@ "--url-2": "url('/my/path')", "--url-3": "url(/my/path)", "--url-4": "url(data:image/png;bm90LWFuZC1pbWFnZQ==)", + "--✅-size": "2em", "--theme-color": "#053" } } diff --git a/plugins/postcss-custom-properties/test/export-properties.mjs b/plugins/postcss-custom-properties/test/export-properties.mjs index 5814fe2e00..30e030b941 100644 --- a/plugins/postcss-custom-properties/test/export-properties.mjs +++ b/plugins/postcss-custom-properties/test/export-properties.mjs @@ -15,5 +15,6 @@ export const customProperties = { '--url-2': 'url(\'/my/path\')', '--url-3': 'url(/my/path)', '--url-4': 'url(data:image/png;bm90LWFuZC1pbWFnZQ==)', + '--✅-size': '2em', '--theme-color': '#053' }; diff --git a/plugins/postcss-custom-properties/test/export-properties.scss b/plugins/postcss-custom-properties/test/export-properties.scss index f1874c60b7..a920224bb1 100644 --- a/plugins/postcss-custom-properties/test/export-properties.scss +++ b/plugins/postcss-custom-properties/test/export-properties.scss @@ -14,4 +14,5 @@ $url-1: url("/my/path"); $url-2: url('/my/path'); $url-3: url(/my/path); $url-4: url(data:image/png;bm90LWFuZC1pbWFnZQ==); +$✅-size: 2em; $theme-color: #053; diff --git a/plugins/postcss-custom-properties/test/import-properties-2.cjs b/plugins/postcss-custom-properties/test/import-properties-2.cjs new file mode 100644 index 0000000000..a9ac867613 --- /dev/null +++ b/plugins/postcss-custom-properties/test/import-properties-2.cjs @@ -0,0 +1,6 @@ +module.exports = { + customProperties: { + '--color': 'rgb(255, 0, 0)', + '--color-2': 'yellow', + }, +}; diff --git a/plugins/postcss-custom-properties/test/import-properties-2.js b/plugins/postcss-custom-properties/test/import-properties-2.js index d260227b2c..a9ac867613 100644 --- a/plugins/postcss-custom-properties/test/import-properties-2.js +++ b/plugins/postcss-custom-properties/test/import-properties-2.js @@ -1,6 +1,6 @@ module.exports = { customProperties: { '--color': 'rgb(255, 0, 0)', - '--color-2': 'yellow' - } + '--color-2': 'yellow', + }, }; diff --git a/plugins/postcss-custom-properties/test/import-properties.cjs b/plugins/postcss-custom-properties/test/import-properties.cjs new file mode 100644 index 0000000000..4b2551bb6b --- /dev/null +++ b/plugins/postcss-custom-properties/test/import-properties.cjs @@ -0,0 +1,6 @@ +module.exports = { + customProperties: { + '--ref-color': 'var(--color)', + '--z-index': 10, + }, +}; diff --git a/plugins/postcss-custom-properties/test/import-properties.js b/plugins/postcss-custom-properties/test/import-properties.js index 85417faf0e..4b2551bb6b 100644 --- a/plugins/postcss-custom-properties/test/import-properties.js +++ b/plugins/postcss-custom-properties/test/import-properties.js @@ -1,6 +1,6 @@ module.exports = { customProperties: { '--ref-color': 'var(--color)', - '--z-index': 10 - } + '--z-index': 10, + }, }; diff --git a/plugins/postcss-custom-properties/test/import-properties.mjs b/plugins/postcss-custom-properties/test/import-properties.mjs new file mode 100644 index 0000000000..78c3b75a2e --- /dev/null +++ b/plugins/postcss-custom-properties/test/import-properties.mjs @@ -0,0 +1,6 @@ +export default { + customProperties: { + '--ref-color': 'var(--color)', + '--z-index': 10, + }, +}; diff --git a/plugins/postcss-custom-properties/tsconfig.json b/plugins/postcss-custom-properties/tsconfig.json new file mode 100644 index 0000000000..f0655e674b --- /dev/null +++ b/plugins/postcss-custom-properties/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": ".", + "module": "es2020" + }, + "include": ["./src/**/*"], + "exclude": ["dist"], +}