Skip to content

Commit 60e0d1b

Browse files
committed
Add extended Sass support
1 parent 769366a commit 60e0d1b

File tree

13 files changed

+1635
-1126
lines changed

13 files changed

+1635
-1126
lines changed

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ Once installed, add this plugin to your `tsconfig.json`:
3838

3939
### Options
4040

41-
| Option | Default value | Description |
42-
| --------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
43-
| `customMatcher` | `"\\.module\\.(sa\|sc\|c)ss$"` | Change the file extensions that this plugin works with. |
44-
| `camelCase` | `false` | Implements the behaviour of the [`camelCase` CSS Loader option](https://github.com/webpack-contrib/css-loader#camelcase) (accepting the same values). |
41+
| Option | Default value | Description |
42+
| --------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
43+
| `customMatcher` | `"\\.module\\.(c\|le\|sa\|sc)ss$"` | Change the file extensions that this plugin works with. |
44+
| `camelCase` | `false` | Implements the behaviour of the [`camelCase` CSS Loader option](https://github.com/webpack-contrib/css-loader#camelcase) (accepting the same values). |
4545

4646
The below is an example that only matches "\*.m.css" files, and [camel-cases dashes](https://github.com/webpack-contrib/css-loader#camelcase).
4747

@@ -65,17 +65,16 @@ The below is an example that only matches "\*.m.css" files, and [camel-cases das
6565

6666
By default, VSCode will use it's own version of TypeScript. To make it work with this plugin, you have two options:
6767

68-
1. Add this plugin to `"typescript.tsserver.pluginPaths"` in settings. Note that this method doesn't currently support
69-
plugin options. This is planned for the [November update](https://github.com/Microsoft/vscode/issues/62876).
68+
1. Use your workspace's version of TypeScript, which will load plugins from your `tsconfig.json` file. This is the recommended approach. For instructions, see: [Using the workspace version of TypeScript](https://code.visualstudio.com/docs/languages/typescript#_using-the-workspace-version-of-typescript).
69+
70+
2. Add this plugin to `"typescript.tsserver.pluginPaths"` in settings. Note that this method doesn't currently support plugin options.
7071

7172
```json
7273
{
7374
"typescript.tsserver.pluginPaths": ["typescript-plugin-css-modules"]
7475
}
7576
```
7677

77-
2. Use your workspace's version of TypeScript, which will load the plugins from your `tsconfig.json` file. For instructions, see: [Using the workspace version of TypeScript](https://code.visualstudio.com/docs/languages/typescript#_using-the-workspace-version-of-typescript).
78-
7978
### Custom definitions
8079

8180
_Note: Create React App users can skip this section if you're using `[email protected]` or higher._
@@ -84,7 +83,7 @@ If your project doesn't already have global declarations for CSS Modules, you wi
8483

8584
Where you store global declarations is up to you. An example might look like: `src/custom.d.ts`.
8685

87-
The below is an example that you can copy, or modify if you use a `customMatcher`.
86+
The below is an example that you can copy or modify. If you use a `customMatcher`, you'll need to modify it.
8887

8988
```ts
9089
declare module '*.module.css' {
@@ -101,4 +100,9 @@ declare module '*.module.sass' {
101100
const classes: { [key: string]: string };
102101
export default classes;
103102
}
103+
104+
declare module '*.module.less' {
105+
const classes: { [key: string]: string };
106+
export default classes;
107+
}
104108
```

package.json

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
],
2121
"scripts": {
2222
"build": "rm -rf ./lib && tsc",
23-
"prepare": "yarn build",
23+
"prepublishOnly": "yarn build",
2424
"test": "jest ./src"
2525
},
2626
"husky": {
@@ -42,24 +42,25 @@
4242
"trailingComma": "all"
4343
},
4444
"dependencies": {
45-
"icss-utils": "^4.0.0",
45+
"@types/node-sass": "^4.11.0",
46+
"icss-utils": "^4.1.0",
4647
"lodash": "^4.17.11",
47-
"postcss": "^7.0.7",
48+
"node-sass": "^4.12.0",
49+
"postcss": "^7.0.16",
4850
"postcss-icss-selectors": "^2.0.3",
49-
"postcss-nested": "^4.1.1",
5051
"strip-css-singleline-comments": "^1.1.0"
5152
},
5253
"devDependencies": {
53-
"@types/jest": "^23.3.12",
54-
"@types/lodash": "^4.14.119",
54+
"@types/jest": "^24.0.13",
55+
"@types/lodash": "^4.14.132",
5556
"@types/node": "^10.12.18",
56-
"husky": "^1.3.1",
57-
"jest": "^23.6.0",
58-
"prettier": "^1.15.3",
59-
"pretty-quick": "^1.8.0",
60-
"ts-jest": "^23.10.5",
61-
"tslint": "^5.12.0",
62-
"typescript": "^3.2.2"
57+
"husky": "^2.3.0",
58+
"jest": "^24.8.0",
59+
"prettier": "^1.17.1",
60+
"pretty-quick": "^1.11.0",
61+
"ts-jest": "^24.0.2",
62+
"tslint": "^5.16.0",
63+
"typescript": "^3.4.5"
6364
},
6465
"peerDependencies": {
6566
"typescript": "^3.0.0"

src/helpers/__tests__/__snapshots__/cssSnapshots.test.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ exports[`utils / cssSnapshots with file 'test.module.scss' createExports should
4545
'nested-class-parent': string;
4646
'child-class': string;
4747
'nested-class-parent--extended': string;
48+
'section-1': string;
49+
'section-2': string;
50+
'section-3': string;
51+
'section-4': string;
52+
'section-5': string;
53+
'section-6': string;
54+
'section-7': string;
55+
'section-8': string;
56+
'section-9': string;
4857
};
4958
export default classes;
5059
"
@@ -59,5 +68,14 @@ Object {
5968
"local-class-inside-local": "file__local-class-inside-local---QdL6b",
6069
"nested-class-parent": "file__nested-class-parent---_ft7G",
6170
"nested-class-parent--extended": "file__nested-class-parent--extended---1642l",
71+
"section-1": "file__section-1---2EiKX",
72+
"section-2": "file__section-2---2f4aZ",
73+
"section-3": "file__section-3---R_Ilj",
74+
"section-4": "file__section-4---3EjYO",
75+
"section-5": "file__section-5---1DSe8",
76+
"section-6": "file__section-6---1RoVP",
77+
"section-7": "file__section-7---l5yMj",
78+
"section-8": "file__section-8---3FEWv",
79+
"section-9": "file__section-9---1TFYE",
6280
}
6381
`;

src/helpers/__tests__/classTransforms.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { transformClasses } from '../classTransforms';
2+
import { CamelCaseOptions } from '../../options';
23

34
describe('utils / classTransforms', () => {
45
const classNames = [

src/helpers/__tests__/createMatchers.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { createMatchers } from '../createMatchers';
2+
import { Options } from '../../options';
23

34
describe('utils / createMatchers', () => {
45
it('should match `customMatcher` regexp', () => {
5-
const options: IOptions = { customMatcher: '\\.css$' };
6+
const options: Options = { customMatcher: '\\.css$' };
67
const { isCSS, isRelativeCSS } = createMatchers(options);
78

89
expect(isCSS('./myfile.css')).toBe(true);
@@ -14,7 +15,7 @@ describe('utils / createMatchers', () => {
1415
});
1516

1617
it('should handle bad `customMatcher` regexp', () => {
17-
const options: IOptions = { customMatcher: '$([a' };
18+
const options: Options = { customMatcher: '$([a' };
1819
const { isCSS, isRelativeCSS } = createMatchers(options);
1920

2021
expect(isCSS('./myfile.module.css')).toBe(true);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
.classA {
2+
color: rebeccapurple;
23
}
34

45
.ClassB {
6+
color: rebeccapurple;
57
}
68

79
.class-c {
10+
color: rebeccapurple;
811
}
912

1013
.parent {
1114
.childA {
15+
color: rebeccapurple;
1216
}
1317
.childB {
1418
.nestedChild {
19+
color: rebeccapurple;
1520
}
1621
}
1722
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,46 @@
11
:global .global-class {
2+
color: rebeccapurple;
23
}
34

45
:global(.global-class-2) {
56
.local-class-inside-global {
7+
color: rebeccapurple;
68
}
79
}
810

911
:local .local-class {
12+
color: rebeccapurple;
1013
}
1114

1215
:local(.local-class-2) {
1316
.local-class-inside-local {
17+
color: rebeccapurple;
1418
}
1519
}
1620

1721
.nested-class-parent {
1822
.child-class {
23+
color: rebeccapurple;
1924
}
2025
&--extended {
26+
color: rebeccapurple;
27+
}
28+
}
29+
30+
$color: rebeccapurple !default;
31+
32+
@for $section from 1 to 10 {
33+
.section-#{$section} {
34+
color: $color;
2135
}
2236
}
2337

2438
// .commented-parent-class {
2539
// .commented-child-class
2640
// }
41+
42+
/*
43+
.commented-parent-class {
44+
.commented-child-class
45+
}
46+
*/

src/helpers/createMatchers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createIsCSS, createIsRelativeCSS } from './cssExtensions';
2+
import { Options } from '../options';
23

3-
export const createMatchers = (options: IOptions = {}) => {
4+
export const createMatchers = (options: Options = {}) => {
45
// Allow custom matchers to be used, and handle bad matcher patterns.
56
let isCSS = createIsCSS();
67
try {

src/helpers/cssExtensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export type isCSSFn = (fileName: string) => boolean;
2-
const DEFAULT_REGEXP = /\.module\.(sa|sc|c)ss$/;
2+
const DEFAULT_REGEXP = /\.module\.(c|le|sa|sc)ss$/;
33

44
const isRelative = (fileName: string) => /^\.\.?($|[\\/])/.test(fileName);
55

src/helpers/cssSnapshots.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,36 @@
11
import { extractICSS, IICSSExports } from 'icss-utils';
22
import * as postcss from 'postcss';
33
import * as postcssIcssSelectors from 'postcss-icss-selectors';
4-
import * as postcssNested from 'postcss-nested';
5-
import * as strip from 'strip-css-singleline-comments/sync';
64
import * as ts_module from 'typescript/lib/tsserverlibrary';
5+
import * as sass from 'node-sass';
76
import { transformClasses } from './classTransforms';
7+
import { Options } from '../options';
88

9-
const processor = postcss(
10-
postcssNested,
11-
postcssIcssSelectors({ mode: 'local' }),
12-
);
9+
const processor = postcss(postcssIcssSelectors({ mode: 'local' }));
1310

14-
export const getClasses = (css: string) => {
11+
const classNameToProperty = (className: string) => `'${className}': string;`;
12+
13+
const flattenClassNames = (
14+
previousValue: string[] = [],
15+
currentValue: string[],
16+
) => previousValue.concat(currentValue);
17+
18+
export const getClasses = (css: string, isLess: boolean = false) => {
1519
try {
16-
const cleanCss = strip(css);
17-
const processedCss = processor.process(cleanCss);
20+
let transformedCss: string;
21+
if (isLess) {
22+
transformedCss = '';
23+
} else {
24+
transformedCss = sass.renderSync({ data: css }).css.toString();
25+
}
26+
const processedCss = processor.process(transformedCss);
1827
return extractICSS(processedCss.root).icssExports;
1928
} catch (e) {
2029
return {};
2130
}
2231
};
23-
const classNameToProperty = (className: string) => `'${className}': string;`;
24-
const flattenClassNames = (
25-
previousValue: string[] = [],
26-
currentValue: string[],
27-
) => previousValue.concat(currentValue);
2832

29-
export const createExports = (classes: IICSSExports, options: IOptions) => `\
33+
export const createExports = (classes: IICSSExports, options: Options) => `\
3034
declare const classes: {
3135
${Object.keys(classes)
3236
.map(transformClasses(options.camelCase))
@@ -40,7 +44,7 @@ export default classes;
4044
export const getDtsSnapshot = (
4145
ts: typeof ts_module,
4246
scriptSnapshot: ts.IScriptSnapshot,
43-
options: IOptions,
47+
options: Options,
4448
) => {
4549
const css = scriptSnapshot.getText(0, scriptSnapshot.getLength());
4650
const classes = getClasses(css);

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import * as ts_module from 'typescript/lib/tsserverlibrary';
33
import { createMatchers } from './helpers/createMatchers';
44
import { isCSSFn } from './helpers/cssExtensions';
55
import { getDtsSnapshot } from './helpers/cssSnapshots';
6+
import { Options } from './options';
67

78
function init({ typescript: ts }: { typescript: typeof ts_module }) {
89
let _isCSS: isCSSFn;
910
function create(info: ts.server.PluginCreateInfo) {
1011
// User options for plugin.
11-
const options: IOptions = info.config.options || {};
12+
const options: Options = info.config.options || {};
1213

1314
// Create matchers using options object.
1415
const { isCSS, isRelativeCSS } = createMatchers(options);

src/@types/global.d.ts renamed to src/options.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
declare interface IOptions {
1+
export interface Options {
22
camelCase?: CamelCaseOptions;
33
customMatcher?: string;
44
}
55

6-
declare type CamelCaseOptions =
6+
export type CamelCaseOptions =
77
| true
88
| 'dashes'
99
| 'dashesOnly'

0 commit comments

Comments
 (0)