From 3d32e73e040bc90a13f4def9319ecc185bd9f061 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 5 Jul 2022 13:25:44 -0400 Subject: [PATCH 1/6] Add more explicit types for the default theme --- defaultTheme.d.ts | 3 +- scripts/generate-types.js | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/defaultTheme.d.ts b/defaultTheme.d.ts index 9172051233cd..2bc9dc718b0c 100644 --- a/defaultTheme.d.ts +++ b/defaultTheme.d.ts @@ -1,3 +1,4 @@ import type { Config } from './types/config' -declare const theme: Config['theme'] +import { DefaultTheme } from './types/generated/default-theme' +declare const theme: Config['theme'] & DefaultTheme export = theme diff --git a/scripts/generate-types.js b/scripts/generate-types.js index ef45233360f6..1adf0695e72e 100644 --- a/scripts/generate-types.js +++ b/scripts/generate-types.js @@ -1,6 +1,7 @@ import prettier from 'prettier' import { corePlugins } from '../src/corePlugins' import colors from '../src/public/colors' +import defaultTheme from '../src/public/default-theme' import fs from 'fs' import path from 'path' @@ -50,3 +51,63 @@ fs.writeFileSync( } ) ) + +function typeCoveringAllValues(value) { + if (Array.isArray(value)) { + let types = value.map(v => typeCoveringAllValues(v)) + return `(${[...new Set(types)].join(' | ')})[]` + } + + if (typeof value === 'object') { + let types = Object.values(value).map(value => typeCoveringAllValues(value)) + return `${[...new Set(types)].join(' | ')}` + } + + if (typeof value === 'string') { + return `string` + } + + return `any` +} + +const defaultThemeTypes = Object.entries(defaultTheme) + .map(([name, value]) => { + if (typeof value === 'string') { + return [name, `string`] + } + + if (typeof value === 'function') { + return [name, null] + } + + if (typeof value === 'object') { + if (Object.keys(value).length === 0) { + return [name, null] + } + + let keyType = Object.keys(value).map((key) => `'${key}'`).join(' | ') + let valueType = typeCoveringAllValues(value) + + return [name, `Record<${keyType}, ${valueType}>`] + } + + return [name, `unknown`] + }) + .filter(([, type]) => type !== null) + .map(([name, type]) => `${name}: ${type}`) + .join('\n') + +fs.writeFileSync( + path.join(process.cwd(), 'types', 'generated', 'default-theme.d.ts'), + prettier.format(` + import { Config } from '../../types' + export type DefaultTheme = Config['theme'] & { ${defaultThemeTypes} } + `, + { + semi: false, + singleQuote: true, + printWidth: 100, + parser: 'typescript', + } + ) +) From 52d4e423403c226c2bb7b116684ffbb98b88f739 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 5 Jul 2022 13:27:17 -0400 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e14df7e014..9bcf4d379b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Don’t prefix selectors in arbitrary variants ([#8773](https://github.com/tailwindlabs/tailwindcss/pull/8773)) - Add support for alpha values in safe list ([#8774](https://github.com/tailwindlabs/tailwindcss/pull/8774)) - Support default `font-weight`s in font size utilities ([#8763](https://github.com/tailwindlabs/tailwindcss/pull/8763)) +- Add more explicit types for the default theme ([#8780](https://github.com/tailwindlabs/tailwindcss/pull/8780)) ## [3.1.4] - 2022-06-21 From f948a991c2b204e939cd14d0300b2fa3a8773145 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 5 Jul 2022 13:31:32 -0400 Subject: [PATCH 3/6] Cleanup --- scripts/generate-types.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/generate-types.js b/scripts/generate-types.js index 1adf0695e72e..485215b1d249 100644 --- a/scripts/generate-types.js +++ b/scripts/generate-types.js @@ -53,14 +53,14 @@ fs.writeFileSync( ) function typeCoveringAllValues(value) { + let union = (values) => [...new Set(values.map((v) => typeCoveringAllValues(v)))].join(' | ') + if (Array.isArray(value)) { - let types = value.map(v => typeCoveringAllValues(v)) - return `(${[...new Set(types)].join(' | ')})[]` + return `(${union(value)})[]` } if (typeof value === 'object') { - let types = Object.values(value).map(value => typeCoveringAllValues(value)) - return `${[...new Set(types)].join(' | ')}` + return union(Object.values(value)) } if (typeof value === 'string') { @@ -85,7 +85,9 @@ const defaultThemeTypes = Object.entries(defaultTheme) return [name, null] } - let keyType = Object.keys(value).map((key) => `'${key}'`).join(' | ') + let keyType = Object.keys(value) + .map((key) => `'${key}'`) + .join(' | ') let valueType = typeCoveringAllValues(value) return [name, `Record<${keyType}, ${valueType}>`] @@ -99,7 +101,8 @@ const defaultThemeTypes = Object.entries(defaultTheme) fs.writeFileSync( path.join(process.cwd(), 'types', 'generated', 'default-theme.d.ts'), - prettier.format(` + prettier.format( + ` import { Config } from '../../types' export type DefaultTheme = Config['theme'] & { ${defaultThemeTypes} } `, From 6398c1892b3f1efde58e7ec6fc750187b2ddbb3f Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 5 Jul 2022 13:56:49 -0400 Subject: [PATCH 4/6] Cleanup code a bit --- scripts/generate-types.js | 26 ++------------------------ scripts/type-utils.js | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 scripts/type-utils.js diff --git a/scripts/generate-types.js b/scripts/generate-types.js index 485215b1d249..fd529cb6f15a 100644 --- a/scripts/generate-types.js +++ b/scripts/generate-types.js @@ -4,6 +4,7 @@ import colors from '../src/public/colors' import defaultTheme from '../src/public/default-theme' import fs from 'fs' import path from 'path' +import * as types from './type-utils' fs.writeFileSync( path.join(process.cwd(), 'types', 'generated', 'corePluginList.d.ts'), @@ -52,24 +53,6 @@ fs.writeFileSync( ) ) -function typeCoveringAllValues(value) { - let union = (values) => [...new Set(values.map((v) => typeCoveringAllValues(v)))].join(' | ') - - if (Array.isArray(value)) { - return `(${union(value)})[]` - } - - if (typeof value === 'object') { - return union(Object.values(value)) - } - - if (typeof value === 'string') { - return `string` - } - - return `any` -} - const defaultThemeTypes = Object.entries(defaultTheme) .map(([name, value]) => { if (typeof value === 'string') { @@ -85,12 +68,7 @@ const defaultThemeTypes = Object.entries(defaultTheme) return [name, null] } - let keyType = Object.keys(value) - .map((key) => `'${key}'`) - .join(' | ') - let valueType = typeCoveringAllValues(value) - - return [name, `Record<${keyType}, ${valueType}>`] + return [name, types.forValue(value)] } return [name, `unknown`] diff --git a/scripts/type-utils.js b/scripts/type-utils.js new file mode 100644 index 000000000000..d9d0e6133c76 --- /dev/null +++ b/scripts/type-utils.js @@ -0,0 +1,27 @@ +export function union(types) { + return [...new Set(types)].join(' | ') +} + +export function unionValues(values) { + return union(values.map(forValue)) +} + +export function forKeys(value) { + return union(Object.keys(value).map((key) => `'${key}'`)) +} + +export function forValue(value) { + if (Array.isArray(value)) { + return `(${unionValues(value)})[]` + } + + if (typeof value === 'object') { + return `Record<${forKeys(value)}, ${unionValues(Object.values(value))}>` + } + + if (typeof value === 'string') { + return `string` + } + + return `any` +} From dba62e7e014d651b893e72c45ef47706b8921c5b Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 5 Jul 2022 13:57:01 -0400 Subject: [PATCH 5/6] Add special cases for a few keys --- scripts/generate-types.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/generate-types.js b/scripts/generate-types.js index fd529cb6f15a..c759a5f8fbff 100644 --- a/scripts/generate-types.js +++ b/scripts/generate-types.js @@ -55,6 +55,16 @@ fs.writeFileSync( const defaultThemeTypes = Object.entries(defaultTheme) .map(([name, value]) => { + // Special cases for slightly more accurate types + if (name === 'keyframes') { + return [name, `Record<${types.forKeys(value)}, Record>`] + } + + if (name === 'fontSize') { + return [name, `Record<${types.forKeys(value)}, [string, { lineHeight: string }]>`] + } + + // General cases if (typeof value === 'string') { return [name, `string`] } @@ -83,6 +93,7 @@ fs.writeFileSync( ` import { Config } from '../../types' export type DefaultTheme = Config['theme'] & { ${defaultThemeTypes} } + export type CSSDeclarationList = Record `, { semi: false, From 80256bfdad5962f02cbc6b4610d0e074056cd6e0 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 5 Jul 2022 13:57:45 -0400 Subject: [PATCH 6/6] Fix order --- scripts/generate-types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate-types.js b/scripts/generate-types.js index c759a5f8fbff..d3e0d303eefc 100644 --- a/scripts/generate-types.js +++ b/scripts/generate-types.js @@ -92,8 +92,8 @@ fs.writeFileSync( prettier.format( ` import { Config } from '../../types' + type CSSDeclarationList = Record export type DefaultTheme = Config['theme'] & { ${defaultThemeTypes} } - export type CSSDeclarationList = Record `, { semi: false,