@cratis/eslint-plugin-components 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/index.js +41 -0
- package/lib/noPrimereactDialog.js +45 -0
- package/lib/noRootBarrelImport.js +48 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @cratis/eslint-plugin-components
|
|
2
|
+
|
|
3
|
+
ESLint rules for projects that consume Cratis Components. Compose these on top of the
|
|
4
|
+
Cratis base config, [`@cratis/eslint-config`](https://www.npmjs.com/package/@cratis/eslint-config).
|
|
5
|
+
|
|
6
|
+
| Rule | What it does |
|
|
7
|
+
|---|---|
|
|
8
|
+
| `no-root-barrel-import` | Disallows importing from the `@cratis/components` root barrel. Use a subpath export (`@cratis/components/CommandDialog`, `@cratis/components/DataPage`, `@cratis/components/Toolbar`, …) — the root pulls the whole optional-peer-heavy surface and hides intent. |
|
|
9
|
+
| `no-primereact-dialog` | Disallows importing `Dialog` from `primereact/dialog`. Use `CommandDialog` from `@cratis/components/CommandDialog`, or `Dialog` from `@cratis/components/Dialogs` — the wrappers add Arc command binding, overlay/focus fixes, and theming. |
|
|
10
|
+
|
|
11
|
+
Both cover `import` and re-`export … from` forms.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
yarn add -D @cratis/eslint-plugin-components @cratis/eslint-config eslint
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// eslint.config.mjs
|
|
23
|
+
import cratis from '@cratis/eslint-config';
|
|
24
|
+
import components from '@cratis/eslint-plugin-components';
|
|
25
|
+
|
|
26
|
+
export default [
|
|
27
|
+
...cratis.configs.consumer,
|
|
28
|
+
...components.configs.recommended,
|
|
29
|
+
// …your project rules
|
|
30
|
+
];
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Options
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
'@cratis/components/no-root-barrel-import': ['error', {
|
|
37
|
+
packageName: '@cratis/components', // barrel to forbid
|
|
38
|
+
allow: [], // exact specifiers to permit
|
|
39
|
+
}],
|
|
40
|
+
'@cratis/components/no-primereact-dialog': ['error', {
|
|
41
|
+
source: 'primereact/dialog', // module to forbid
|
|
42
|
+
}],
|
|
43
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { noPrimereactDialog } from './lib/noPrimereactDialog.js';
|
|
3
|
+
import { noRootBarrelImport } from './lib/noRootBarrelImport.js';
|
|
4
|
+
|
|
5
|
+
const { version } = createRequire(import.meta.url)('./package.json');
|
|
6
|
+
|
|
7
|
+
// A single flat-config plugin object — meta + rules + self-referencing configs — per the
|
|
8
|
+
// ESLint flat-config plugin convention. The default export IS the plugin, so consumers
|
|
9
|
+
// get `components.meta`, `components.rules`, and `components.configs` directly. Composes
|
|
10
|
+
// on top of @cratis/eslint-config.
|
|
11
|
+
const plugin = {
|
|
12
|
+
meta: { name: '@cratis/eslint-plugin-components', version },
|
|
13
|
+
rules: {
|
|
14
|
+
'no-primereact-dialog': noPrimereactDialog,
|
|
15
|
+
'no-root-barrel-import': noRootBarrelImport,
|
|
16
|
+
},
|
|
17
|
+
configs: {},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// configs reference the plugin itself, so they are assigned after it exists.
|
|
21
|
+
//
|
|
22
|
+
// import cratis from '@cratis/eslint-config';
|
|
23
|
+
// import components from '@cratis/eslint-plugin-components';
|
|
24
|
+
// export default [...cratis.configs.consumer, ...components.configs.recommended];
|
|
25
|
+
Object.assign(plugin.configs, {
|
|
26
|
+
recommended: [
|
|
27
|
+
{
|
|
28
|
+
name: '@cratis/components/recommended',
|
|
29
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
30
|
+
plugins: { '@cratis/components': plugin },
|
|
31
|
+
rules: {
|
|
32
|
+
'@cratis/components/no-primereact-dialog': 'error',
|
|
33
|
+
'@cratis/components/no-root-barrel-import': 'error',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export default plugin;
|
|
40
|
+
export const { configs, rules, meta } = plugin;
|
|
41
|
+
export { noPrimereactDialog, noRootBarrelImport };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const DEFAULTS = { source: 'primereact/dialog' };
|
|
2
|
+
|
|
3
|
+
// Disallow importing PrimeReact's Dialog directly. Cratis Components wraps it with
|
|
4
|
+
// behavior (Arc command binding, overlay/focus fixes, theming) via CommandDialog and the
|
|
5
|
+
// Dialogs surface; reaching past those to `primereact/dialog` bypasses all of it. Covers
|
|
6
|
+
// `import` and re-`export … from` forms.
|
|
7
|
+
export const noPrimereactDialog = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Disallow importing Dialog from primereact/dialog; use the Cratis Components dialog wrappers.',
|
|
12
|
+
recommended: true,
|
|
13
|
+
url: 'https://github.com/Cratis/Components/blob/main/ESLint/README.md',
|
|
14
|
+
},
|
|
15
|
+
schema: [{
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
source: { type: 'string' },
|
|
19
|
+
},
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
}],
|
|
22
|
+
messages: {
|
|
23
|
+
useWrapper: "Do not import from '{{source}}'. Use CommandDialog from '@cratis/components/CommandDialog', or Dialog from '@cratis/components/Dialogs'.",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
create(context) {
|
|
27
|
+
const options = { ...DEFAULTS, ...(context.options[0] ?? {}) };
|
|
28
|
+
|
|
29
|
+
const check = node => {
|
|
30
|
+
const source = node.source;
|
|
31
|
+
if (!source || typeof source.value !== 'string') return;
|
|
32
|
+
if (source.value === options.source) {
|
|
33
|
+
context.report({ node: source, messageId: 'useWrapper', data: { source: options.source } });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
ImportDeclaration: check,
|
|
39
|
+
ExportNamedDeclaration: check,
|
|
40
|
+
ExportAllDeclaration: check,
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default noPrimereactDialog;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const DEFAULTS = { packageName: '@cratis/components', allow: [] };
|
|
2
|
+
|
|
3
|
+
// Disallow importing from the Cratis Components root barrel. The package exposes
|
|
4
|
+
// purpose-built subpath exports (`@cratis/components/CommandDialog`,
|
|
5
|
+
// `@cratis/components/DataPage`, `@cratis/components/Toolbar`, …); importing the root
|
|
6
|
+
// pulls the whole optional-peer-heavy surface and hides intent. Covers `import` and
|
|
7
|
+
// re-`export … from` forms.
|
|
8
|
+
export const noRootBarrelImport = {
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Disallow importing from the @cratis/components root barrel; use a subpath export.',
|
|
13
|
+
recommended: true,
|
|
14
|
+
url: 'https://github.com/Cratis/Components/blob/main/ESLint/README.md',
|
|
15
|
+
},
|
|
16
|
+
schema: [{
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
packageName: { type: 'string' },
|
|
20
|
+
allow: { type: 'array', items: { type: 'string' } },
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
}],
|
|
24
|
+
messages: {
|
|
25
|
+
useSubpath: "Import from a '{{packageName}}' subpath (e.g. '{{packageName}}/CommandDialog'), not the package root barrel.",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
create(context) {
|
|
29
|
+
const options = { ...DEFAULTS, ...(context.options[0] ?? {}) };
|
|
30
|
+
const allow = new Set(options.allow);
|
|
31
|
+
|
|
32
|
+
const check = node => {
|
|
33
|
+
const source = node.source;
|
|
34
|
+
if (!source || typeof source.value !== 'string') return;
|
|
35
|
+
if (source.value === options.packageName && !allow.has(source.value)) {
|
|
36
|
+
context.report({ node: source, messageId: 'useSubpath', data: { packageName: options.packageName } });
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
ImportDeclaration: check,
|
|
42
|
+
ExportNamedDeclaration: check,
|
|
43
|
+
ExportAllDeclaration: check,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default noRootBarrelImport;
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cratis/eslint-plugin-components",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Cratis Components ESLint rules: import from subpaths, not the root barrel, and use the Cratis dialog wrappers instead of primereact/dialog. Compose on top of @cratis/eslint-config.",
|
|
5
|
+
"author": "Cratis",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Cratis/Components"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"eslint",
|
|
17
|
+
"eslintplugin",
|
|
18
|
+
"eslint-plugin",
|
|
19
|
+
"cratis",
|
|
20
|
+
"components"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js",
|
|
24
|
+
"lib",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"exports": {
|
|
28
|
+
"./package.json": "./package.json",
|
|
29
|
+
".": "./index.js"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "yarn g:test",
|
|
33
|
+
"ci": "yarn g:test"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"eslint": ">=9"
|
|
37
|
+
}
|
|
38
|
+
}
|