@taiga-ui/eslint-plugin-experience-next 0.255.0 → 0.256.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.
@@ -14,6 +14,7 @@ var unicorn = require('eslint-plugin-unicorn');
14
14
  var unusedImports = require('eslint-plugin-unused-imports');
15
15
  var globals = require('globals');
16
16
  var tseslint = require('typescript-eslint');
17
+ var path = require('node:path');
17
18
 
18
19
  function getDefaultExportFromCjs (x) {
19
20
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -348,6 +349,118 @@ var src = {
348
349
 
349
350
  var eslint = /*@__PURE__*/getDefaultExportFromCjs(src);
350
351
 
352
+ const MESSAGE_ID = 'no-deep-imports';
353
+ const ERROR_MESSAGE = 'Deep imports of Taiga UI packages are prohibited';
354
+ const DEFAULT_OPTIONS = {
355
+ currentProject: '',
356
+ deepImport: String.raw`(?<=^@taiga-ui/[\w-]+)(/.+)$`,
357
+ ignoreImports: [],
358
+ importDeclaration: '^@taiga-ui*',
359
+ projectName: String.raw`(?<=^@taiga-ui/)([-\w]+)`
360
+ };
361
+ const config = {
362
+ create(context) {
363
+ const {
364
+ currentProject,
365
+ deepImport,
366
+ ignoreImports,
367
+ importDeclaration,
368
+ projectName
369
+ } = {
370
+ ...DEFAULT_OPTIONS,
371
+ ...(context.options[0] || {})
372
+ };
373
+ const isDeepImport = source => !!source.match(new RegExp(deepImport, 'g'))?.length;
374
+ const isInsideTheSameEntryPoint = source => {
375
+ const filePath = path.relative(context.getCwd(), context.getFilename()).replaceAll(/\\+/g, '/');
376
+ const [currentFileProjectName] = currentProject && new RegExp(currentProject, 'g').exec(filePath) || [];
377
+ const [importSourceProjectName] = source.match(new RegExp(projectName, 'g')) || [];
378
+ return Boolean(currentFileProjectName && importSourceProjectName && currentFileProjectName === importSourceProjectName);
379
+ };
380
+ const shouldIgnore = source => ignoreImports.some(p => source.match(new RegExp(p, 'g')));
381
+ return {
382
+ [`ImportDeclaration[source.value=/${importDeclaration}/]`]({
383
+ source: sourceNode
384
+ }) {
385
+ const importSource = sourceNode?.value || '';
386
+ if (!isDeepImport(importSource) || isInsideTheSameEntryPoint(importSource) || shouldIgnore(importSource)) {
387
+ return;
388
+ }
389
+ context.report({
390
+ fix: fixer => {
391
+ const [start, end] = sourceNode.range;
392
+ return fixer.replaceTextRange([start + 1, end - 1],
393
+ // keeps quotes
394
+ importSource.replaceAll(new RegExp(deepImport, 'g'), ''));
395
+ },
396
+ messageId: MESSAGE_ID,
397
+ node: sourceNode
398
+ });
399
+ }
400
+ };
401
+ },
402
+ meta: {
403
+ docs: {
404
+ description: ERROR_MESSAGE
405
+ },
406
+ fixable: 'code',
407
+ messages: {
408
+ [MESSAGE_ID]: ERROR_MESSAGE
409
+ },
410
+ schema: [{
411
+ additionalProperties: false,
412
+ properties: {
413
+ currentProject: {
414
+ description: 'RegExp string to pick out current project name of processed file',
415
+ type: 'string'
416
+ },
417
+ deepImport: {
418
+ description: 'RegExp string to pick out deep import part',
419
+ type: 'string'
420
+ },
421
+ ignoreImports: {
422
+ description: 'RegExp string to exclude import declarations which is selected by importDeclaration-option',
423
+ items: {
424
+ type: 'string'
425
+ },
426
+ type: 'array'
427
+ },
428
+ importDeclaration: {
429
+ description: 'RegExp string to detect import declarations for which this rule should be applied',
430
+ type: 'string'
431
+ }
432
+ },
433
+ type: 'object'
434
+ }],
435
+ type: 'problem'
436
+ }
437
+ };
438
+
439
+ const plugin = {
440
+ configs: {},
441
+ meta: {
442
+ name: '@taiga-ui/experience'
443
+ },
444
+ rules: {
445
+ 'no-deep-imports': config
446
+ }
447
+ };
448
+ // https://eslint.org/docs/latest/extend/plugins
449
+ // assign configs here so we can reference `plugin`
450
+ Object.assign(plugin.configs, {
451
+ recommended: [{
452
+ plugins: {
453
+ '@taiga-ui/experience': plugin
454
+ },
455
+ rules: {
456
+ '@taiga-ui/experience/no-deep-imports': ['error', {
457
+ currentProject: String.raw`(?<=projects/)([-\w]+)`,
458
+ ignoreImports: [String.raw`\?raw`, '@taiga-ui/testing/cypress', '@taiga-ui/testing/setup-jest']
459
+ }]
460
+ }
461
+ }]
462
+ });
463
+
351
464
  let angularVersion = 16;
352
465
  const tsconfig = projectJsonExist('tsconfig.eslint.json') || projectJsonExist('tsconfig.json');
353
466
  const parserOptions = tsconfig ? {
@@ -366,11 +479,12 @@ try {
366
479
  } catch {}
367
480
  var eslint_config = tseslint.config(progress.configs['recommended-ci'], require('eslint-plugin-de-morgan').configs.recommended, require('eslint-plugin-import').flatConfigs.recommended, require('eslint-plugin-import').flatConfigs.typescript, require('eslint-plugin-promise').configs['flat/recommended'], {
368
481
  ignores: ['*/icons/all.ts', '**/tests-report/**', '**/snapshots/**', '**/test-results/**', '**/.nx/**', '404.html', '*.jpg', '*.svg', '*.less', '*.scss', '*.txt', '*.png', '*.jpg', '*.webmanifest', '*.pdf', '*.mp3', '*.ogv', '*.mp4', '*.ico', '*.xml', '*.md', 'LICENSE', 'jest.preset.js', '*.config.js', 'node_modules', 'dist', '**/node_modules/**', '**/*@dasherize__/**', '**/coverage/**', 'eslintrc.js', '.eslintrc.js', '**/*.d.ts', '**/dist/**', '**/docs/**', '**/bin/**', '.cache/**', '.git/**', '.idea/**']
369
- }, eslint.configs.recommended, tseslint.configs.recommended, require('eslint-config-prettier'), {
482
+ }, eslint.configs.recommended, plugin.configs.recommended, tseslint.configs.recommended, require('eslint-config-prettier'), {
370
483
  files: ['**/*.ts', '**/*.js'],
371
484
  plugins: {
372
485
  '@stylistic': stylistic,
373
486
  '@stylistic/ts': stylisticTs,
487
+ '@taiga-ui/experience': plugin,
374
488
  'decorator-position': require('eslint-plugin-decorator-position'),
375
489
  perfectionist: require('eslint-plugin-perfectionist'),
376
490
  prettier,
@@ -12,6 +12,7 @@ import unicorn from 'eslint-plugin-unicorn';
12
12
  import unusedImports from 'eslint-plugin-unused-imports';
13
13
  import globals from 'globals';
14
14
  import tseslint from 'typescript-eslint';
15
+ import path from 'node:path';
15
16
 
16
17
  function getDefaultExportFromCjs (x) {
17
18
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -346,6 +347,118 @@ var src = {
346
347
 
347
348
  var eslint = /*@__PURE__*/getDefaultExportFromCjs(src);
348
349
 
350
+ const MESSAGE_ID = 'no-deep-imports';
351
+ const ERROR_MESSAGE = 'Deep imports of Taiga UI packages are prohibited';
352
+ const DEFAULT_OPTIONS = {
353
+ currentProject: '',
354
+ deepImport: String.raw`(?<=^@taiga-ui/[\w-]+)(/.+)$`,
355
+ ignoreImports: [],
356
+ importDeclaration: '^@taiga-ui*',
357
+ projectName: String.raw`(?<=^@taiga-ui/)([-\w]+)`
358
+ };
359
+ const config = {
360
+ create(context) {
361
+ const {
362
+ currentProject,
363
+ deepImport,
364
+ ignoreImports,
365
+ importDeclaration,
366
+ projectName
367
+ } = {
368
+ ...DEFAULT_OPTIONS,
369
+ ...(context.options[0] || {})
370
+ };
371
+ const isDeepImport = source => !!source.match(new RegExp(deepImport, 'g'))?.length;
372
+ const isInsideTheSameEntryPoint = source => {
373
+ const filePath = path.relative(context.getCwd(), context.getFilename()).replaceAll(/\\+/g, '/');
374
+ const [currentFileProjectName] = currentProject && new RegExp(currentProject, 'g').exec(filePath) || [];
375
+ const [importSourceProjectName] = source.match(new RegExp(projectName, 'g')) || [];
376
+ return Boolean(currentFileProjectName && importSourceProjectName && currentFileProjectName === importSourceProjectName);
377
+ };
378
+ const shouldIgnore = source => ignoreImports.some(p => source.match(new RegExp(p, 'g')));
379
+ return {
380
+ [`ImportDeclaration[source.value=/${importDeclaration}/]`]({
381
+ source: sourceNode
382
+ }) {
383
+ const importSource = sourceNode?.value || '';
384
+ if (!isDeepImport(importSource) || isInsideTheSameEntryPoint(importSource) || shouldIgnore(importSource)) {
385
+ return;
386
+ }
387
+ context.report({
388
+ fix: fixer => {
389
+ const [start, end] = sourceNode.range;
390
+ return fixer.replaceTextRange([start + 1, end - 1],
391
+ // keeps quotes
392
+ importSource.replaceAll(new RegExp(deepImport, 'g'), ''));
393
+ },
394
+ messageId: MESSAGE_ID,
395
+ node: sourceNode
396
+ });
397
+ }
398
+ };
399
+ },
400
+ meta: {
401
+ docs: {
402
+ description: ERROR_MESSAGE
403
+ },
404
+ fixable: 'code',
405
+ messages: {
406
+ [MESSAGE_ID]: ERROR_MESSAGE
407
+ },
408
+ schema: [{
409
+ additionalProperties: false,
410
+ properties: {
411
+ currentProject: {
412
+ description: 'RegExp string to pick out current project name of processed file',
413
+ type: 'string'
414
+ },
415
+ deepImport: {
416
+ description: 'RegExp string to pick out deep import part',
417
+ type: 'string'
418
+ },
419
+ ignoreImports: {
420
+ description: 'RegExp string to exclude import declarations which is selected by importDeclaration-option',
421
+ items: {
422
+ type: 'string'
423
+ },
424
+ type: 'array'
425
+ },
426
+ importDeclaration: {
427
+ description: 'RegExp string to detect import declarations for which this rule should be applied',
428
+ type: 'string'
429
+ }
430
+ },
431
+ type: 'object'
432
+ }],
433
+ type: 'problem'
434
+ }
435
+ };
436
+
437
+ const plugin = {
438
+ configs: {},
439
+ meta: {
440
+ name: '@taiga-ui/experience'
441
+ },
442
+ rules: {
443
+ 'no-deep-imports': config
444
+ }
445
+ };
446
+ // https://eslint.org/docs/latest/extend/plugins
447
+ // assign configs here so we can reference `plugin`
448
+ Object.assign(plugin.configs, {
449
+ recommended: [{
450
+ plugins: {
451
+ '@taiga-ui/experience': plugin
452
+ },
453
+ rules: {
454
+ '@taiga-ui/experience/no-deep-imports': ['error', {
455
+ currentProject: String.raw`(?<=projects/)([-\w]+)`,
456
+ ignoreImports: [String.raw`\?raw`, '@taiga-ui/testing/cypress', '@taiga-ui/testing/setup-jest']
457
+ }]
458
+ }
459
+ }]
460
+ });
461
+
349
462
  let angularVersion = 16;
350
463
  const tsconfig = projectJsonExist('tsconfig.eslint.json') || projectJsonExist('tsconfig.json');
351
464
  const parserOptions = tsconfig ? {
@@ -364,11 +477,12 @@ try {
364
477
  } catch {}
365
478
  var eslint_config = tseslint.config(progress.configs['recommended-ci'], require('eslint-plugin-de-morgan').configs.recommended, require('eslint-plugin-import').flatConfigs.recommended, require('eslint-plugin-import').flatConfigs.typescript, require('eslint-plugin-promise').configs['flat/recommended'], {
366
479
  ignores: ['*/icons/all.ts', '**/tests-report/**', '**/snapshots/**', '**/test-results/**', '**/.nx/**', '404.html', '*.jpg', '*.svg', '*.less', '*.scss', '*.txt', '*.png', '*.jpg', '*.webmanifest', '*.pdf', '*.mp3', '*.ogv', '*.mp4', '*.ico', '*.xml', '*.md', 'LICENSE', 'jest.preset.js', '*.config.js', 'node_modules', 'dist', '**/node_modules/**', '**/*@dasherize__/**', '**/coverage/**', 'eslintrc.js', '.eslintrc.js', '**/*.d.ts', '**/dist/**', '**/docs/**', '**/bin/**', '.cache/**', '.git/**', '.idea/**']
367
- }, eslint.configs.recommended, tseslint.configs.recommended, require('eslint-config-prettier'), {
480
+ }, eslint.configs.recommended, plugin.configs.recommended, tseslint.configs.recommended, require('eslint-config-prettier'), {
368
481
  files: ['**/*.ts', '**/*.js'],
369
482
  plugins: {
370
483
  '@stylistic': stylistic,
371
484
  '@stylistic/ts': stylisticTs,
485
+ '@taiga-ui/experience': plugin,
372
486
  'decorator-position': require('eslint-plugin-decorator-position'),
373
487
  perfectionist: require('eslint-plugin-perfectionist'),
374
488
  prettier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.255.0",
3
+ "version": "0.256.0",
4
4
  "description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./eslint.config.cjs.js",
@@ -9,7 +9,7 @@
9
9
  "@smarttools/eslint-plugin-rxjs": ">=1.0.18",
10
10
  "@stylistic/eslint-plugin": ">=4.0.1",
11
11
  "@stylistic/eslint-plugin-ts": ">=4.0.1",
12
- "@typescript-eslint/eslint-plugin": ">=8.24.1",
12
+ "@typescript-eslint/eslint-plugin": ">=8.25.0",
13
13
  "angular-eslint": ">=19.1.0",
14
14
  "eslint": ">=9.21.0",
15
15
  "eslint-config-prettier": ">=10.0.1",
@@ -27,7 +27,7 @@
27
27
  "eslint-plugin-unicorn": ">=57.0.0",
28
28
  "eslint-plugin-unused-imports": ">=4.1.4",
29
29
  "globals": ">=16.0.0",
30
- "typescript-eslint": ">=8.24.1"
30
+ "typescript-eslint": ">=8.25.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
@@ -0,0 +1,15 @@
1
+ import type { Linter } from 'eslint';
2
+ declare const plugin: {
3
+ configs: {
4
+ readonly recommended: {
5
+ readonly rules: Readonly<Linter.RulesRecord>;
6
+ };
7
+ };
8
+ meta: {
9
+ name: string;
10
+ };
11
+ rules: {
12
+ 'no-deep-imports': import("eslint").Rule.RuleModule;
13
+ };
14
+ };
15
+ export default plugin;
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const config: Rule.RuleModule;
3
+ export default config;