Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement griffel stylelint config #437

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: entrypoint and preset config fixes",
"packageName": "@griffel/postcss-syntax",
"email": "lingfan.gao@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "major",
"comment": "Initial release",
"packageName": "@griffel/stylelint-config",
"email": "lingfan.gao@microsoft.com",
"dependentChangeType": "patch"
}
3 changes: 1 addition & 2 deletions packages/postcss-syntax/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@
"@griffel/babel-preset": "^1.5.0",
"@babel/helper-plugin-utils": "^7.12.13",
"postcss": "^8.4.29"
},
"main": "index.js"
}
}
2 changes: 1 addition & 1 deletion packages/postcss-syntax/src/createSyntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { BabelPluginOptions } from '@griffel/babel-preset';
* @returns a postcss custom syntax
*/
export function createSyntax(options: BabelPluginOptions): postcss.Syntax {
const extendedParse: postcss.Parser = (css, opts) => parse(css, { ...opts, ...options });
const extendedParse: postcss.Parser = (css, opts) => parse(css, { ...opts, griffelPreset: { ...options } });

return {
stringify,
Expand Down
10 changes: 6 additions & 4 deletions packages/postcss-syntax/src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { BabelPluginOptions } from '@griffel/babel-preset';

export type PostCSSParserOptions = Pick<postcss.ProcessOptions<postcss.Document | postcss.Root>, 'from' | 'map'>;

export interface ParserOptions extends PostCSSParserOptions, BabelPluginOptions {}
export interface ParserOptions extends PostCSSParserOptions {
griffelPreset?: BabelPluginOptions;
}

export const parse = (css: string | { toString(): string }, opts?: ParserOptions) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { from: filename = 'postcss-syntax.styles.ts', map, ...griffelPluginOptions } = opts ?? {};
const { from: filename = 'postcss-syntax.styles.ts', griffelPreset } = opts ?? {};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

postcss passes some untyped properties here - it will fail the griffel config validator, so I've put it all the preset configuration in a separate property


const code = css.toString();
const { metadata } = transformSync(code, {
filename,
pluginOptions: {
...griffelPluginOptions,
...griffelPreset,
generateMetadata: true,
},
});
Expand Down
1 change: 0 additions & 1 deletion packages/postcss-syntax/src/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { GRIFFEL_SRC_RAW } from './constants';
export const stringify: postcss.Stringifier = root => {
const originalSource = root.raw(GRIFFEL_SRC_RAW);
if (originalSource) {
console.log(originalSource);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oopsie

return originalSource;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/stylelint-config/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]]
}
18 changes: 18 additions & 0 deletions packages/stylelint-config/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
45 changes: 45 additions & 0 deletions packages/stylelint-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Standard Stylelint config for Griffel

This is a a standard [stylelint](https://stylelint.io/) that can be used to lint Griffel CSS in JS.
It uses the [Griffel postcss syntax](https://github.com/microsoft/griffel/tree/main/packages/postcss-syntax) as
a base config for any stylelint integration.

## Usage

Simply extend this configuration in your stylelint config

```json
{
"extends": "@griffe/stylelint-config",
"rules": {
"alpha-value-notation": "number"
}
}
```

## Different module source or imports

If you are using a different module source or import name like the below example:

```ts
import { foo /* makeStyles */ } from '@foo/foo'; /* @griffel/react */

export const useStyles = foo({
root: {
color: 'red',
},
});
```

There is no way to customize postcss custom syntaxes, so the only way to make linting these CSS in JS files
possible is to create the griffel postcss custom syntax from a factory that can be configured. For this to work
you'll need to use a javascript stylelint config `.stylelintrc.js`

```js
module.exports {
customSyntax: require('@griffel/postcss-syntax').createSyntax({ modules: [{ moduleSource: '@foo/foo', importName: 'foo' }] }),
rules: {
"alpha-value-notation": "number"
}
}
```
16 changes: 16 additions & 0 deletions packages/stylelint-config/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable */
export default {
displayName: 'eslint-plugin',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
coverageDirectory: '../../coverage/packages/eslint-plugin',
};
13 changes: 13 additions & 0 deletions packages/stylelint-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@griffel/stylelint-config",
"version": "0.0.0",
"description": "A default stylelint config for Griffel",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/griffel"
},
"dependencies": {
"@griffel/postcss-syntax": "^1.0.0"
}
}
55 changes: 55 additions & 0 deletions packages/stylelint-config/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@griffel/stylelint-config",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/stylelint-config/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/stylelint-config/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["{workspaceRoot}/coverage/packages/stylelint-config"],
"options": {
"jestConfig": "packages/stylelint-config/jest.config.ts",
"passWithNoTests": true
}
},
"build": {
"executor": "@nrwl/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/stylelint-config",
"tsConfig": "packages/stylelint-config/tsconfig.lib.json",
"packageJson": "packages/stylelint-config/package.json",
"main": "packages/stylelint-config/src/index.ts",
"updateBuildableProjectDepsInPackageJson": false,
"assets": [
"packages/stylelint-config/README.md",
{
"glob": "LICENSE.md",
"input": ".",
"output": "."
}
]
}
},
"type-check": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/babel-preset",
"commands": [
{
"command": "tsc -b --pretty"
}
],
"outputPath": []
}
}
},
"tags": []
}
5 changes: 5 additions & 0 deletions packages/stylelint-config/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import griffelSyntax from '@griffel/postcss-syntax';

export default {
customSyntax: griffelSyntax,
};
81 changes: 81 additions & 0 deletions packages/stylelint-config/src/stylelint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as stylelint from 'stylelint';
import griffelStylelintConfig from './index';
import { createSyntax } from '@griffel/postcss-syntax';

describe('stylelint', () => {
it('should lint makeStyles with standard config', async () => {
expect.assertions(1);
const config = {
...griffelStylelintConfig,
rules: {
'selector-anb-no-unmatchable': [true],
},
};

const code = `
import { makeStyles } from '@griffel/react';

const useStyles = makeStyles({
root: {
':nth-child(0)': {
color: 'red',
}
}
})
`;

const { results } = await stylelint.lint({ code, config });
const warnings = results.map(result => result.warnings).flat();
expect(warnings).toMatchInlineSnapshot(`
Array [
Object {
"column": 11,
"endColumn": 24,
"endLine": 5,
"line": 5,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The starting line is always correct, however the rest of the locations are wrong because most of the stylelint default rules calculate additional offsets internally for better reporting when linting real CSS files.

This doesn't work too well with the griffel postcss syntax since we lose the mapping to the entire style slot. Example:

https://github.com/stylelint/stylelint/blob/0b686f289ab436eac64aaa78eb1646d38a153294/lib/rules/selector-anb-no-unmatchable/index.js#L91C8-L100

That rule even uses a third party parser for the selector and doesn't really use postcss at all

"rule": "selector-anb-no-unmatchable",
"severity": "error",
"text": "Unexpected unmatchable An+B selector \\":nth-child\\" (selector-anb-no-unmatchable)",
},
]
`);
});

it('should lint makeStyles with different module source', async () => {
expect.assertions(1);
const config = {
customSyntax: createSyntax({ modules: [{ moduleSource: '@foo/foo', importName: 'foo' }] }),
rules: {
'selector-anb-no-unmatchable': [true],
},
};

const code = `
import { foo } from '@foo/foo';

const useStyles = foo({
root: {
':nth-child(0)': {
color: 'red',
}
}
})
`;

const { results } = await stylelint.lint({ code, config });
const warnings = results.map(result => result.warnings).flat();
expect(warnings).toMatchInlineSnapshot(`
Array [
Object {
"column": 11,
"endColumn": 24,
"endLine": 5,
"line": 5,
"rule": "selector-anb-no-unmatchable",
"severity": "error",
"text": "Unexpected unmatchable An+B selector \\":nth-child\\" (selector-anb-no-unmatchable)",
},
]
`);
});
});
22 changes: 22 additions & 0 deletions packages/stylelint-config/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
11 changes: 11 additions & 0 deletions packages/stylelint-config/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": ["node", "environment"]
},
"exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"],
"include": ["**/*.ts"]
}
20 changes: 20 additions & 0 deletions packages/stylelint-config/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node", "environment"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx",
"**/*.test.js",
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts",
"jest.config.ts"
]
}
1 change: 1 addition & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@griffel/jest-serializer": ["packages/jest-serializer/src/index.ts"],
"@griffel/next-extraction-plugin": ["packages/next-extraction-plugin/src/index.ts"],
"@griffel/postcss-syntax": ["packages/postcss-syntax/src/index.ts"],
"@griffel/stylelint-config": ["packages/stylelint-config/src/index.ts"],
"@griffel/style-types": ["packages/style-types/src/index.ts"],
"@griffel/react": ["packages/react/src/index.ts"],
"@griffel/webpack-extraction-plugin": ["packages/webpack-extraction-plugin/src/index.ts"],
Expand Down
1 change: 1 addition & 0 deletions workspace.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@griffel/next-extraction-plugin": "packages/next-extraction-plugin",
"@griffel/postcss-syntax": "packages/postcss-syntax",
"@griffel/react": "packages/react",
"@griffel/stylelint-config": "packages/stylelint-config",
"@griffel/style-types": "packages/style-types",
"@griffel/webpack-extraction-plugin": "packages/webpack-extraction-plugin",
"@griffel/webpack-loader": "packages/webpack-loader",
Expand Down
Loading