Skip to content

Commit

Permalink
Merge pull request #21 from ckeditor/ck/14329
Browse files Browse the repository at this point in the history
Feature (eslint-plugin-ckeditor5-rules): Created the `ckeditor5-rules/no-scoped-imports-within-package` that disallows using scoped import (like `"@ckeditor/ckeditor5-*"`) to the same package where the import declaration is located. Closes ckeditor/ckeditor5#14329.

Other (eslint-config-ckeditor5): Enabled the `ckeditor5-rules/no-scoped-imports-within-package` in the ESLint configuration.

Internal (eslint-config-ckeditor5): Bumped ESLint parser configuration to target the ES2020 grammar.

Internal (eslint-plugin-ckeditor5-rules): Aligned all tests for all ESLint rules to target the ES2020 grammar.
  • Loading branch information
psmyrek committed Jun 19, 2023
2 parents 9d99dbc + 9c4c96d commit af2205c
Show file tree
Hide file tree
Showing 33 changed files with 863 additions and 59 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -35,7 +35,7 @@
"@ckeditor/ckeditor5-dev-release-tools": "^38.0.0",
"eslint": "^7.0.0",
"husky": "^8.0.2",
"lint-staged": "^12.0.0",
"lint-staged": "^13.0.0",
"listr2": "^6.5.0"
},
"resolutions": {
Expand Down
12 changes: 11 additions & 1 deletion packages/eslint-config-ckeditor5/.eslintrc.js
Expand Up @@ -10,7 +10,7 @@ const METHODS_THAT_USE_AS_CONST_INSTEAD_OF_RETURN_TYPE = [ 'requires', 'pluginNa
module.exports = {
extends: 'eslint:recommended',
parserOptions: {
ecmaVersion: 2018,
ecmaVersion: 2020,
sourceType: 'module'
},
env: {
Expand Down Expand Up @@ -314,6 +314,7 @@ module.exports = {
'ckeditor5-rules/no-relative-imports': 'error',
'ckeditor5-rules/ckeditor-error-message': 'error',
'ckeditor5-rules/no-cross-package-imports': 'error',
'ckeditor5-rules/no-scoped-imports-within-package': 'error',
'ckeditor5-rules/use-require-for-debug-mode-imports': 'error',
'ckeditor5-rules/no-istanbul-in-debug-code': 'error',

Expand Down Expand Up @@ -507,6 +508,15 @@ module.exports = {
rules: {
'ckeditor5-rules/no-build-extensions': 'error'
}
},
{
files: [
'**/docs/**/*.@(js|ts)',
'**/tests/**/*.@(js|ts)'
],
rules: {
'ckeditor5-rules/no-scoped-imports-within-package': 'off'
}
}
]
};
1 change: 1 addition & 0 deletions packages/eslint-plugin-ckeditor5-rules/lib/index.js
Expand Up @@ -11,6 +11,7 @@ module.exports = {
'ckeditor-error-message': require( './rules/ckeditor-error-message' ),
'ckeditor-imports': require( './rules/ckeditor-imports' ),
'no-cross-package-imports': require( './rules/no-cross-package-imports' ),
'no-scoped-imports-within-package': require( './rules/no-scoped-imports-within-package' ),
'license-header': require( './rules/license-header' ),
'use-require-for-debug-mode-imports': require( './rules/use-require-for-debug-mode-imports' ),
'non-public-members-as-internal': require( './rules/non-public-members-as-internal' ),
Expand Down
@@ -0,0 +1,97 @@
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

'use strict';

const upath = require( 'upath' );
const fs = require( 'fs-extra' );

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Disallow scoped import like "@ckeditor/*" to the same package where the import declaration is located.',
category: 'CKEditor5',
// eslint-disable-next-line max-len
url: 'https://ckeditor.com/docs/ckeditor5/latest/framework/contributing/code-style.html#imports-within-a-package-ckeditor5-rulesno-scoped-imports-within-package'
},
schema: []
},
create( context ) {
const handler = callbackFactory( context );

return {
ImportDeclaration: handler,
ExportNamedDeclaration: handler,
ExportAllDeclaration: handler
};
}
};

/**
* @param {Object} context
* @return {Function}
*/
function callbackFactory( context ) {
return node => {
if ( !node.source ) {
return;
}

const importedPackageName = node.source.value;

const isScopedImport =
importedPackageName.startsWith( '@ckeditor/ckeditor5' ) ||
importedPackageName.startsWith( 'ckeditor5' );

if ( !isScopedImport ) {
return;
}

const directory = upath.dirname( context.getFilename() );
const cwd = upath.normalizeTrim( context.getCwd() );
const packageName = getPackageName( directory, cwd );

if ( !packageName ) {
return;
}

const isScopedImportToSamePackage =
importedPackageName === packageName ||
importedPackageName.startsWith( `${ packageName }/` );

if ( isScopedImportToSamePackage ) {
context.report( {
node,
message: 'Scoped import like "@ckeditor/*" to the same package where the import declaration is located is disallowed.'
} );
}
};
}

/**
* @param {String} directory
* @param {String} cwd
* @return {String|null}
*/
function getPackageName( directory, cwd ) {
const packageJsonPath = upath.join( directory, 'package.json' );

if ( fs.pathExistsSync( packageJsonPath ) ) {
const packageJson = fs.readJsonSync( packageJsonPath, { throws: false } );

if ( packageJson ) {
return packageJson.name;
}
}

const parentDirectory = upath.dirname( directory );

if ( parentDirectory.startsWith( cwd ) ) {
return getPackageName( parentDirectory, cwd );
}

return null;
}
8 changes: 6 additions & 2 deletions packages/eslint-plugin-ckeditor5-rules/package.json
Expand Up @@ -29,13 +29,17 @@
},
"devDependencies": {
"eslint": "^7.0.0",
"eslint-config-ckeditor5": "^5.0.1"
"eslint-config-ckeditor5": "^5.0.1",
"glob": "^10.2.5",
"lodash": "^4.17.21"
},
"peerDependencies": {
"eslint": ">=7.0.0"
},
"dependencies": {
"@es-joy/jsdoccomment": "^0.36.1",
"@typescript-eslint/parser": "^5.52.0"
"@typescript-eslint/parser": "^5.52.0",
"fs-extra": "^11.1.1",
"upath": "^2.0.1"
}
}
36 changes: 16 additions & 20 deletions packages/eslint-plugin-ckeditor5-rules/tests/fixture-loader.js
Expand Up @@ -4,31 +4,27 @@
*/

const fs = require( 'fs' );
const path = require( 'path' );

const fixtureTypes = [
'valid',
'invalid'
];
const upath = require( 'upath' );
const { globSync } = require( 'glob' );
const _ = require( 'lodash' );

module.exports = function fixtureLoader( ruleName ) {
const fixtures = {};

for ( const fixtureType of fixtureTypes ) {
const typeDir = path.join( __dirname, 'fixtures', ruleName, fixtureType );
const cwd = upath.join( __dirname, 'fixtures', ruleName );

fixtures[ fixtureType ] = fs.readdirSync( typeDir ).reduce( ( output, filename ) => {
const fixturePath = path.join( typeDir, filename );
const fixtureContent = fs.readFileSync( fixturePath, 'utf-8' );
const fixtureKey = filename
return globSync( '**/*.js', { cwd } )
.map( upath.normalize )
.reduce( ( result, fixtureRelativePath ) => {
const fixtureKey = fixtureRelativePath
.replace( '.js', '' )
.replace( /-/g, '_' );
.replace( /-/g, '_' )
.split( upath.sep );

output[ fixtureKey ] = fixtureContent;
const fixturePath = upath.join( cwd, fixtureRelativePath );
const fixtureContent = fs.readFileSync( fixturePath, 'utf-8' );

return output;
return _.set( result, fixtureKey, {
path: fixturePath,
content: fixtureContent
} );
}, {} );
}

return fixtures;
};
@@ -0,0 +1,3 @@
{
"name": "@ckeditor/ckeditor5-feature-nested"
}
@@ -0,0 +1,32 @@
import name1 from '@ckeditor/ckeditor5-feature-nested';
import name2 from '@ckeditor/ckeditor5-feature-nested/src';

import * as name3 from '@ckeditor/ckeditor5-feature-nested';
import * as name4 from '@ckeditor/ckeditor5-feature-nested/src';

import { name5 } from '@ckeditor/ckeditor5-feature-nested';
import { name6 } from '@ckeditor/ckeditor5-feature-nested/src';

import { name as name7 } from '@ckeditor/ckeditor5-feature-nested';
import { name as name8 } from '@ckeditor/ckeditor5-feature-nested/src';

import { default as name9 } from '@ckeditor/ckeditor5-feature-nested';
import { default as name10 } from '@ckeditor/ckeditor5-feature-nested/src';

import '@ckeditor/ckeditor5-feature-nested';
import '@ckeditor/ckeditor5-feature-nested/src';

export * from '@ckeditor/ckeditor5-feature-nested';
export * from '@ckeditor/ckeditor5-feature-nested/src';

export * as name11 from '@ckeditor/ckeditor5-feature-nested';
export * as name12 from '@ckeditor/ckeditor5-feature-nested/src';

export { name13 } from '@ckeditor/ckeditor5-feature-nested';
export { name14 } from '@ckeditor/ckeditor5-feature-nested/src';

export { name as name15 } from '@ckeditor/ckeditor5-feature-nested';
export { name as name16 } from '@ckeditor/ckeditor5-feature-nested/src';

export { default as name17 } from '@ckeditor/ckeditor5-feature-nested';
export { default as name18 } from '@ckeditor/ckeditor5-feature-nested/src';
@@ -0,0 +1,3 @@
{
"name": "ckeditor5-feature-short"
}
@@ -0,0 +1,32 @@
import name1 from 'ckeditor5-feature-short';
import name2 from 'ckeditor5-feature-short/src';

import * as name3 from 'ckeditor5-feature-short';
import * as name4 from 'ckeditor5-feature-short/src';

import { name5 } from 'ckeditor5-feature-short';
import { name6 } from 'ckeditor5-feature-short/src';

import { name as name7 } from 'ckeditor5-feature-short';
import { name as name8 } from 'ckeditor5-feature-short/src';

import { default as name9 } from 'ckeditor5-feature-short';
import { default as name10 } from 'ckeditor5-feature-short/src';

import 'ckeditor5-feature-short';
import 'ckeditor5-feature-short/src';

export * from 'ckeditor5-feature-short';
export * from 'ckeditor5-feature-short/src';

export * as name11 from 'ckeditor5-feature-short';
export * as name12 from 'ckeditor5-feature-short/src';

export { name13 } from 'ckeditor5-feature-short';
export { name14 } from 'ckeditor5-feature-short/src';

export { name as name15 } from 'ckeditor5-feature-short';
export { name as name16 } from 'ckeditor5-feature-short/src';

export { default as name17 } from 'ckeditor5-feature-short';
export { default as name18 } from 'ckeditor5-feature-short/src';
@@ -0,0 +1,3 @@
{
"name": "@ckeditor/ckeditor5-feature"
}
@@ -0,0 +1,32 @@
import name1 from '@ckeditor/ckeditor5-feature';
import name2 from '@ckeditor/ckeditor5-feature/src';

import * as name3 from '@ckeditor/ckeditor5-feature';
import * as name4 from '@ckeditor/ckeditor5-feature/src';

import { name5 } from '@ckeditor/ckeditor5-feature';
import { name6 } from '@ckeditor/ckeditor5-feature/src';

import { name as name7 } from '@ckeditor/ckeditor5-feature';
import { name as name8 } from '@ckeditor/ckeditor5-feature/src';

import { default as name9 } from '@ckeditor/ckeditor5-feature';
import { default as name10 } from '@ckeditor/ckeditor5-feature/src';

import '@ckeditor/ckeditor5-feature';
import '@ckeditor/ckeditor5-feature/src';

export * from '@ckeditor/ckeditor5-feature';
export * from '@ckeditor/ckeditor5-feature/src';

export * as name11 from '@ckeditor/ckeditor5-feature';
export * as name12 from '@ckeditor/ckeditor5-feature/src';

export { name13 } from '@ckeditor/ckeditor5-feature';
export { name14 } from '@ckeditor/ckeditor5-feature/src';

export { name as name15 } from '@ckeditor/ckeditor5-feature';
export { name as name16 } from '@ckeditor/ckeditor5-feature/src';

export { default as name17 } from '@ckeditor/ckeditor5-feature';
export { default as name18 } from '@ckeditor/ckeditor5-feature/src';
@@ -0,0 +1,3 @@
{
"name": "@ckeditor/ckeditor5-feature-nested"
}
@@ -0,0 +1,43 @@
import name1 from 'no-scoped-import';
import name2 from './no-scoped-imports';
import name3 from '../no-scoped-import';

import * as name4 from 'no-scoped-import';
import * as name5 from './no-scoped-imports';
import * as name6 from '../no-scoped-import';

import { name7 } from 'no-scoped-import';
import { name8 } from './no-scoped-imports';
import { name9 } from '../no-scoped-import';

import { name as name10 } from 'no-scoped-import';
import { name as name11 } from './no-scoped-imports';
import { name as name12 } from '../no-scoped-import';

import { default as name13 } from 'no-scoped-import';
import { default as name14 } from './no-scoped-imports';
import { default as name15 } from '../no-scoped-import';

import 'no-scoped-import';
import './no-scoped-imports';
import '../no-scoped-import';

export * from 'no-scoped-import';
export * from './no-scoped-imports';
export * from '../no-scoped-import';

export * as name16 from 'no-scoped-import';
export * as name17 from './no-scoped-imports';
export * as name18 from '../no-scoped-import';

export { name19 } from 'no-scoped-import';
export { name20 } from './no-scoped-imports';
export { name21 } from '../no-scoped-import';

export { name as name22 } from 'no-scoped-import';
export { name as name23 } from './no-scoped-imports';
export { name as name24 } from '../no-scoped-import';

export { default as name25 } from 'no-scoped-import';
export { default as name26 } from './no-scoped-imports';
export { default as name27 } from '../no-scoped-import';

0 comments on commit af2205c

Please sign in to comment.