From f2a97de936cfedd7d1eac11ee6ad6ff6a60d545e Mon Sep 17 00:00:00 2001 From: Brody McKee Date: Sun, 4 Dec 2022 11:11:42 +1100 Subject: [PATCH] feat: enable compatibility with `noUncheckedIndexedAccess` --- README.md | 25 +++++----- .../__snapshots__/getDtsSnapshot.test.ts.snap | 49 +++++++++++++++++++ src/helpers/__tests__/getDtsSnapshot.test.ts | 29 +++++++++++ src/helpers/classTransforms.ts | 2 +- src/helpers/createDtsExports.ts | 11 +++-- src/index.ts | 5 +- src/options.ts | 3 +- 7 files changed, 103 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 30d526f..28dea85 100644 --- a/README.md +++ b/README.md @@ -97,17 +97,18 @@ const b = styles['my_other-class']; Please note that no options are required. However, depending on your configuration, you may need to customise these options. -| Option | Default value | Description | -| -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------ | -| `classnameTransform` | `asIs` | See [`classnameTransform`](#classnameTransform) below. | -| `customMatcher` | `"\\.module\\.(c\|le\|sa\|sc)ss$"` | Changes the file extensions that this plugin processes. | -| `customRenderer` | `false` | See [`customRenderer`](#customRenderer) below. | -| `customTemplate` | `false` | See [`customTemplate`](#customTemplate) below. | -| `goToDefinition` | `false` | Enables jump to definition, with limited compatibility. See [`goToDefinition`](#goToDefinition) below. | -| `namedExports` | `true` | Enables named exports for compatible classnames. | -| `dotenvOptions` | `{}` | Provides options for [`dotenv`](https://github.com/motdotla/dotenv#options). | -| `postcssOptions` | `{}` | See [`postcssOptions`](#postcssOptions) below. | -| `rendererOptions` | `{}` | See [`rendererOptions`](#rendererOptions) below. | +| Option | Default value | Description | +| -------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `classnameTransform` | `asIs` | See [`classnameTransform`](#classnameTransform) below. | +| `customMatcher` | `"\\.module\\.(c\|le\|sa\|sc)ss$"` | Changes the file extensions that this plugin processes. | +| `customRenderer` | `false` | See [`customRenderer`](#customRenderer) below. | +| `customTemplate` | `false` | See [`customTemplate`](#customTemplate) below. | +| `goToDefinition` | `false` | Enables jump to definition, with limited compatibility. See [`goToDefinition`](#goToDefinition) below. | +| `noUncheckedIndexedAccess` | `false` | Enable for compatibility with TypeScript's `noUncheckedIndexedAccess`. | +| `namedExports` | `true` | Enables named exports for compatible classnames. | +| `dotenvOptions` | `{}` | Provides options for [`dotenv`](https://github.com/motdotla/dotenv#options). | +| `postcssOptions` | `{}` | See [`postcssOptions`](#postcssOptions) below. | +| `rendererOptions` | `{}` | See [`rendererOptions`](#rendererOptions) below. | ```json { @@ -195,7 +196,7 @@ The `classes` object represents all the classnames extracted from the CSS Module #### `goToDefinition` -This allows an editor like Visual Studio Code to jump to a classname's definition (file and line). +This allows an editor like Visual Studio Code to go to a classname's definition (file and line). This is experimental, and only works with Sass (for now) and may not always work as expected. diff --git a/src/helpers/__tests__/__snapshots__/getDtsSnapshot.test.ts.snap b/src/helpers/__tests__/__snapshots__/getDtsSnapshot.test.ts.snap index ec56b19..5800497 100644 --- a/src/helpers/__tests__/__snapshots__/getDtsSnapshot.test.ts.snap +++ b/src/helpers/__tests__/__snapshots__/getDtsSnapshot.test.ts.snap @@ -855,3 +855,52 @@ exports[`utils / cssSnapshots with includePaths in stylus options should find ex "include-path": "include-path-module__include-path---2f2uR", } `; + +exports[`utils / cssSnapshots with noUncheckedIndexedAccess enabled should return a dts file with only possibly undefined strings 1`] = ` +"declare let classes: { + 'localClassInsideGlobal'?: string; + 'localClass'?: string; + 'localClass2'?: string; + 'localClassInsideLocal'?: string; + 'reservedWords'?: string; + 'default'?: string; + 'const'?: string; + 'nestedClassParent'?: string; + 'childClass'?: string; + 'nestedClassParentExtended'?: string; + 'section1'?: string; + 'section2'?: string; + 'section3'?: string; + 'section4'?: string; + 'section5'?: string; + 'section6'?: string; + 'section7'?: string; + 'section8'?: string; + 'section9'?: string; + 'classWithMixin'?: string; + 'appLogo'?: string; + 'appLogo'?: string; +}; +export default classes; +export let localClassInsideGlobal?: string; +export let localClass?: string; +export let localClass2?: string; +export let localClassInsideLocal?: string; +export let reservedWords?: string; +export let nestedClassParent?: string; +export let childClass?: string; +export let nestedClassParentExtended?: string; +export let section1?: string; +export let section2?: string; +export let section3?: string; +export let section4?: string; +export let section5?: string; +export let section6?: string; +export let section7?: string; +export let section8?: string; +export let section9?: string; +export let classWithMixin?: string; +export let appLogo?: string; +export let appLogo?: string; +" +`; diff --git a/src/helpers/__tests__/getDtsSnapshot.test.ts b/src/helpers/__tests__/getDtsSnapshot.test.ts index 906a1ad..8372e20 100644 --- a/src/helpers/__tests__/getDtsSnapshot.test.ts +++ b/src/helpers/__tests__/getDtsSnapshot.test.ts @@ -253,4 +253,33 @@ describe('utils / cssSnapshots', () => { expect(dts).toMatchSnapshot(); }); }); + + describe('with noUncheckedIndexedAccess enabled', () => { + const fileName = join(__dirname, 'fixtures', 'test.module.scss'); + const css = readFileSync(fileName, 'utf8'); + const options: Options = { + classnameTransform: 'camelCaseOnly', + noUncheckedIndexedAccess: true, + }; + + const cssExports = getCssExports({ + css, + fileName, + logger, + options, + processor, + compilerOptions, + }); + + it('should return a dts file with only possibly undefined strings', () => { + const dts = createDtsExports({ + cssExports, + fileName, + logger, + options, + }); + expect(dts).not.toMatch(/\w'?: string/); + expect(dts).toMatchSnapshot(); + }); + }); }); diff --git a/src/helpers/classTransforms.ts b/src/helpers/classTransforms.ts index 1059482..31e4b2f 100644 --- a/src/helpers/classTransforms.ts +++ b/src/helpers/classTransforms.ts @@ -2,7 +2,7 @@ import camelCase from 'lodash.camelcase'; import { ClassnameTransformOptions } from '../options'; // The below is based on the CSS Modules implementation found here: -// https://github.com/webpack-contrib/css-loader/blob/master/lib/compile-exports.js +// https://github.com/webpack-contrib/css-loader const dashCase = (className: string): string => className.replace(/-+(\w)/g, (_match: string, firstLetter: string) => diff --git a/src/helpers/createDtsExports.ts b/src/helpers/createDtsExports.ts index 3b1b759..abbb737 100644 --- a/src/helpers/createDtsExports.ts +++ b/src/helpers/createDtsExports.ts @@ -8,10 +8,6 @@ import { Logger } from './logger'; const isValidVariable = (className: string) => VALID_VARIABLE_REGEXP.test(className); -const classNameToProperty = (className: string) => `'${className}': string;`; -const classNameToNamedExport = (className: string) => - `export let ${className}: string;`; - const flattenClassNames = ( previousValue: string[] = [], currentValue: string[], @@ -30,6 +26,13 @@ export const createDtsExports = ({ }): string => { const classes = cssExports.classes; + const possiblyUndefined = Boolean(options.noUncheckedIndexedAccess); + + const classNameToProperty = (className: string) => + `'${className}'${possiblyUndefined ? '?' : ''}: string;`; + const classNameToNamedExport = (className: string) => + `export let ${className}${possiblyUndefined ? '?' : ''}: string;`; + const processedClasses = Object.keys(classes) .map(transformClasses(options.classnameTransform)) .reduce(flattenClassNames, []); diff --git a/src/index.ts b/src/index.ts index 5ca091b..928a018 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,9 +32,8 @@ function init({ typescript: ts }: { typescript: typeof tsModule }) { process.chdir(directory); // User options for plugin. - - const config = info.config as { options?: Options }; - const options = config.options ?? {}; + const options: Options = + (info.config as { options?: Options }).options ?? {}; logger.log(`options: ${JSON.stringify(options)}`); // Load environment variables like SASS_PATH. diff --git a/src/options.ts b/src/options.ts index d85c7db..730d566 100644 --- a/src/options.ts +++ b/src/options.ts @@ -27,8 +27,9 @@ export interface Options { dotenvOptions?: DotenvConfigOptions; goToDefinition?: boolean; namedExports?: boolean; + noUncheckedIndexedAccess?: boolean; postcssOptions?: PostcssOptions; - /** @deprecated To align with other projects. */ + /** @deprecated To align with naming in other projects. */ postCssOptions?: PostcssOptions; rendererOptions?: RendererOptions; }