代码风格
CKEditor 5 开发环境 已启用 ESLint 作为预提交钩子和 CI。这意味着代码风格问题会自动检测。此外,每个存储库中都有 .editorconfig
文件,以自动调整您的 IDE 设置(如果配置为读取它们)。
以下是对这些规则的简要概述。
# 通用
LF 用于行尾。绝不使用 CRLF。
建议的最大行长为 120 个字符。它不能超过 140 个字符。
# 空白
没有尾随空格。空行不应包含任何空格。
在括号内和运算符前后的空白
function foo( a, b, c, d, e ) {
if ( a > b ) {
c = ( d + e ) * 2;
}
}
foo( bar() );
空括号没有空白
const a = () => {
// Statements.
// ...
};
a();
冒号和分号之前没有空白
let a, b;
a( 1, 2, 3 );
for ( const i = 0; i < 100; i++ ) {
// Statements.
// ...
}
# 缩进
使用制表符缩进,包括代码和注释。绝不使用空格。
如果要使代码可读,请在 IDE 中将制表符设置为4 个空格。
class Bar {
a() {
while ( b in a ) {
if ( b == c ) {
// Statements.
// ...
}
}
}
}
多行条件。每行使用一个制表符
if (
some != really.complex &&
condition || with &&
( multiple == lines )
) {
// Statements.
// ...
}
while (
some != really.complex &&
condition || with &&
( multiple == lines )
) {
// Statements.
// ...
}
我们尽力避免复杂的条件。作为经验法则,我们首先建议找到一种方法将复杂性从条件中移出,例如,移到一个单独的函数中,并为这种条件中的每个“句子”进行提前返回。
然而,过度做也不好,有时这样的条件可以完美地阅读(这是这里的最终目标)。
# 大括号
大括号与头部语句在同一行开始,并与其对齐结束
function a() {
// Statements.
// ...
}
if ( a ) {
// Statements.
// ...
} else if ( b ) {
// Statements.
// ...
} else {
// Statements.
// ...
}
try {
// Statements.
// ...
} catch ( e ) {
// Statements.
// ...
}
# 空行
代码应该像书一样阅读,因此在代码的“段落”之间放置空行。这是一个开放的和上下文相关的规则,但一些建议是将以下部分分开
- 变量、类和函数声明,
if()
、for()
和类似块,- 算法的步骤,
return
语句,- 注释部分(注释应该用空行开头,但如果它们本身是“段落”,也应该用空行结尾),
- 等等。
示例
class Foo extends Plugin {
constructor( editor ) {
super( editor );
/**
* Some documentation...
*/
this.foo = new Foo();
/**
* Some documentation...
*/
this.isBar = false;
}
method( bar ) {
const editor = this.editor;
const selection = editor.model.document.selection;
for ( const range of selection.getRanges() ) {
const position = range.start;
if ( !position ) {
return false;
}
// At this stage this and this need to happen.
// We considered doing this differently, but it has its shortcomings.
// Refer to the tests and issue #3456 to learn more.
const result = editor.model.checkSomething( position );
if ( result ) {
return true;
}
}
return true;
}
performAlgorithm() {
// 1. Do things A and B.
this.a();
this.b();
// 2. Check C.
if ( c() ) {
d();
}
// 3. Check whether we are fine.
const areWeFine = 1 || 2 || 3;
this.check( areWeFine );
// 4. Finalize.
this.finalize( areWeFine );
return areWeFine;
}
}
# 多行语句和调用
每当有多行函数调用时
- 将第一个参数放在新行中。
- 将每个参数放在单独的行中,缩进一个制表符。
- 将最后一个右括号放在新行中,与调用的开头位于同一缩进级别。
示例
const myObj = new MyClass(
'Some long parameters',
'To make this',
'Multi line'
);
fooBar(
() => {
// Statements.
// ...
}
);
fooBar(
new MyClass(
'Some long parameters',
'To make this',
'Multi line'
)
);
fooBar(
'A very long string',
() => {
// Some kind of a callback.
// ...
},
5,
new MyClass(
'It looks well',
paramA,
paramB,
new ShortClass( 2, 3, 5 ),
'Even when nested'
)
);
这些示例只是展示了如何构造这样的函数调用。但是,最好避免它们。
通常建议避免使用接受超过 3 个参数的函数。相反,最好将它们包装在对象中,以便所有参数都可以命名。
还建议将此类长语句拆分为多个较短的语句,例如,通过将一些较长的参数提取到单独的变量中。
# 字符串
使用单引号
const a = 'I\'m an example for quotes';
长字符串可以用加号 (+
) 连接
const html =
'Line 1\n' +
'Line 2\n' +
'Line 3';
或者你可以使用模板字符串。在这种情况下,第二行和第三行将缩进
const html =
`Line 1
Line 2
Line 3`;
HTML 字符串应使用缩进以提高可读性
const html =
`<p>
<span>${ a }</span>
</p>`;
# 注释
- 注释总是以空行开头。
- 注释以大写第一个字母开头,并在末尾需要一个句点(因为它们是句子)。
- 注释的文本开头必须有一个空格,紧跟在注释标记之后。
块注释 (/** ... */
) 用于文档目的。星号与空格对齐
/**
* Documentation for the following method.
*
* @returns {Object} Something.
*/
someMethod() {
// Statements.
// ...
}
所有其他注释使用行注释 (//
)
// A comment about the following statement.
foo();
// Multiple line comments
// go through several
// line comments as well.
与工单或问题相关的注释不应完全描述整个问题。相反,应该使用简短的描述,以及括号中的工单号
// Do this otherwise because of a Safari bug. (#123)
foo();
# 代码风格检查
CKEditor 5 开发环境使用 ESLint 和 stylelint。
一些有用的链接
- 使用内联注释禁用 ESLint.
- CKEditor 5 ESLint 预设 (npm:
eslint-config-ckeditor5
). - CKEditor 5 stylelint 预设 (npm:
stylelint-config-ckeditor5
).
避免在现有代码上使用自动代码格式化程序。自动格式化您正在编辑的代码是可以的,但您不应该更改已编写代码的格式,以免污染您的 PR。您也不应该仅仅依赖于自动更正。
# 可见性级别
每个类属性(包括方法、符号、获取器或设置器)可以是公共的、受保护的或私有的。默认可见性为公共的,因此您不应该记录属性是公共的——没有必要这样做。
私有属性适用其他规则
- 在类原型(或以任何其他方式)中公开的私有和受保护属性的名称应以下划线为前缀。
- 在记录未添加到类原型(或以任何其他方式公开)的私有变量时,应使用
//
注释,并且不需要使用@private
。 - 符号属性(如
this[ Symbol( 'symbolName' ) ]
)应记录为@property {Type} _symbolName
。
示例
class Foo {
/**
* The constructor (public, as its visibility isn't defined).
*/
constructor() {
/**
* Public property.
*/
this.foo = 1;
/**
* Protected property.
*
* @protected
*/
this._bar = 1;
/**
* @private
* @property {Number} _bom
*/
this[ Symbol( 'bom' ) ] = 1;
}
/**
* @private
*/
_somePrivateMethod() {}
}
// Some private helper.
//
// @returns {Number}
function doSomething() {
return 1;
}
# 可访问性
下表显示了属性的可访问性
类 | 包 | 子类 | 世界 | |
---|---|---|---|---|
@public |
是 | 是 | 是 | 是 |
@protected |
是 | 是 | 是 | 否 |
@private |
是 | 否 | 否 | 否 |
(是——可访问,否——不可访问)
例如,受保护属性可以从定义它的类本身、从其整个包以及从其子类(即使它们不在同一个包中)访问。
受保护属性和方法通常用于可测试性。由于测试位于与代码相同的包中,因此它们可以访问这些属性。
# 获取器
您可以使用 ES6 获取器来简化类 API
class Position {
// More methods.
// ...
get offset() {
return this.path[ this.path.length - 1 ];
}
}
获取器应该感觉像一个自然属性。在创建获取器时,有几个建议要遵循
- 它们应该很快。
- 它们不应抛出异常。
- 它们不应更改对象状态。
- 它们不应每次都返回新对象实例(因此
foo.bar == foo.bar
为真)。如果可能,为第一次调用创建一个新实例并将其缓存是可以的。
# 类定义中的顺序
在类定义中,按以下顺序排列方法和属性:
- 构造函数。
- Getter 和 Setter。
- 迭代器。
- 公共实例方法。
- 公共静态方法。
- 受保护的实例方法。
- 受保护的静态方法。
- 私有实例方法。
- 私有静态方法。
每个组内的顺序由实现者决定。
# 测试
测试有一些特殊的规则和技巧。
# 测试组织
-
在测试文件中始终使用外部的
describe()
。不允许在最外层的describe()
之外使用任何全局变量,尤其是钩子(beforeEach()
、after()
等)。 -
最外层的
describe()
调用应该创建有意义的组,这样当所有测试一起运行时,就可以在代码库中识别出失败的测试用例。例如describe( 'Editor', () => { describe( 'constructor()', () => { it( /* ... */ ); } ); // More test cases. // ... } );
使用诸如
utils
之类的标题是不好的,因为整个项目中有多个实用程序。Table utils
会更好。 -
测试描述 (
it()
) 应该像文档一样编写(你做了什么以及应该发生什么),例如,“当单击 X 按钮时,foo 对话框关闭”。另外,测试描述中的“...case 1”、“...case 2” 并没有帮助。 -
避免使用诸如“当两个范围合并时不会崩溃”之类的测试描述。相反,解释预期实际发生的情况。例如:“当两个范围合并时,保留 1 个范围。”
-
大多数情况下,使用诸如“正确地”、“工作正常”之类的词语是一种代码异味。考虑一下需求 - 在编写需求时,你不会说特性 X 应该“工作正常”。你会记录它应该如何工作。
-
理想情况下,应该能够仅通过阅读测试描述来重新创建算法。
-
避免在一个
it()
下涵盖多个用例。在一个测试中使用多个断言是可以的,但不要测试,例如,方法foo()
被调用一次、两次、三次等时的工作方式。每个用例都应该有一个单独的测试。 -
每个测试都应该在自身之后进行清理,包括销毁所有编辑器并删除所有已添加的元素。
# 测试实现
-
避免使用实时超时。尽可能使用 模拟计时器。超时会使测试变得非常慢。
-
但是,不要过度优化(尤其是因为性能在测试中不是优先事项)。在大多数情况下,为每个
it()
创建一个单独的编辑器是完全可以的(因此建议这样做)。 -
我们的目标是覆盖所有不同场景的 100%。覆盖代码中 100% 的分支不是这里的目标 - 它是覆盖真实场景的副产品。
想想这个:当你通过向现有函数调用添加参数来修复 bug 时,你不会影响代码覆盖率(该行无论如何都被调用了)。但是,你遇到了 bug,这意味着你的测试套件没有覆盖它。因此,必须为该代码更改创建测试。
-
它应该是
expect( x ).to.equal( y )
。而不是:。expect( x ).to.be.equal( y )
-
当使用 Sinon 间谍时,请注意断言和错误消息的可读性。
-
使用命名间谍,例如
const someCallbackSpy = sinon.spy().named( 'someCallback' ); const myMethodSpy = sinon.spy( obj, 'myMethod' );
-
expect( myMethodSpy ).to.be.calledOnce // expected myMethod to be called once but was called twice
-
# 命名
# JavaScript 代码名称
变量、函数、命名空间、参数以及所有未记录的用例必须以 小驼峰命名法 命名
let a;
let myLongNamedVariable = true;
function foo() {}
function longNamedFunction( example, longNamedParameter ) {}
类必须以 大驼峰命名法 命名
class MyClass() {}
const a = new MyClass();
Mixin 必须以 大驼峰命名法 命名,后缀为“Mixin”
const SomeMixin = {
method1: /* ... */,
method2: /* ... */
};
全局命名空间变量必须以 全大写 命名
const CKEDITOR_TRANSLATIONS = {};
# 私有属性和方法
私有属性和方法以下划线为前缀
CKEDITOR._counter;
something._doInternalTask();
# 方法和函数
方法和函数几乎总是动词或动作
// Good
execute();
this.getNextNumber()
// Bad
this.editable();
this.status();
# 属性和变量
属性和变量几乎总是名词
const editor = this.editor;
this.name;
this.editable;
布尔属性和变量始终以助动词为前缀
this.isDirty;
this.hasChildren;
this.canObserve;
this.mustRefresh;
# 按钮、命令和插件
# 按钮
所有按钮都应该遵循动词 + 名词或名词约定。示例
- 动词 + 名词约定
insertTable
selectAll
uploadImage
- 名词约定
bold
mediaEmbed
restrictedEditing
# 命令
至于命令,则比较棘手。与按钮相比,它们的名称组合更多。示例
- 与功能相关的约定
- 基于名词的情况
codeBlock
fontColor
shiftEnter
- 基于动词的情况
indent
removeFormat
selectAll
- 基于名词的情况
- 功能 + 子功能约定
imageStyle
imageTextAlternative
tableAlignment
对于命令,名词 + 动词(或功能 + 动作)命名约定不应使用,因为听起来不自然(做什么 与 做什么)。在大多数情况下,正确的名称应该以动作开头,然后是功能名称
checkTodoList
insertTable
uploadImage
# 插件
插件应该遵循功能或功能 + 子功能约定。示例
- 功能约定
Bold
Paragraph
SpecialCharacters
- 功能 + 子功能约定
ImageResize
ListProperties
TableClipboard
插件必须以 大驼峰命名法 命名。
# 快捷键
对于局部变量,普遍接受的简短版本可以用于长名称
const attr, doc, el, fn, deps, err, id, args, uid, evt, env;
以下是唯一接受的用于属性名称的简短版本
this.lang;
this.config;
this.id;
this.env;
# 首字母缩略词和专有名词
首字母缩略词以及部分专有名词自然以大写形式书写。这可能与上面描述的代码风格规则相冲突 - 特别是在需要在变量或类名中包含首字母缩略词或专有名词时。在这种情况下,应该遵循以下规则
- 首字母缩略词
- 如果在变量名的开头,则全部小写:
let domError
。 - 如果在类名的开头,则默认使用小驼峰命名法:
class DomError
。 - 如果在变量或类名内,则默认使用小驼峰命名法:
function getDomError()
。
- 如果在变量名的开头,则全部小写:
- 专有名词
- 如果在变量名的开头,则全部小写:
let ckeditorError
。 - 如果在类名的开头,则使用原始的大小写:
class CKEditorError
。 - 如果在变量或类名内,则使用原始的大小写:
function getCKEditorError()
。
- 如果在变量名的开头,则全部小写:
但是,两个字母的首字母缩略词和专有名词(如果最初以大写形式书写)应该以大写形式书写。例如:getUI
,而不是 getUi
。
两个最常用的会导致问题的首字母缩略词
- DOM - 它应该是例如
getDomNode()
, - HTML - 它应该是例如
toHtml()
。
# CSS 类
CSS 类命名模式基于 BEM 方法和代码风格。所有名称都以小写形式书写,单词之间可以选择使用破折号 (-
)。
顶级构建块以强制性的 ck-
前缀开头
.ck-dialog
.ck-toolbar
.ck-dropdown-menu
属于块命名空间的元素以双下划线 (__
) 分隔
.ck-dialog__header
.ck-toolbar__group
.ck-dropdown-menu__button-collapser
修饰符以单个下划线 (_
) 分隔。键值修饰符
遵循 block-or-element_key_value
命名模式
/* Block modifier */
.ck-dialog_hidden
/* Element modifier */
.ck-toolbar__group_collapsed
/* Block modifier as key_value */
.ck-dropdown-menu_theme_some-theme
在 HTML 中
<div class="ck-reset_all ck-dialog ck-dialog_theme_lark ck-dialog_visible">
<div class="ck-dialog__top ck-dialog__top_small">
<h1 class="ck-dialog__top-title">Title of the dialog</h1>
...
</div>
...
</div>
# ID 属性
HTML ID 属性命名模式遵循 CSS 类 命名指南。每个 ID 必须以 ck-
前缀开头,并且由以小写形式书写的破折号分隔 (-
) 的单词组成
<div id="ck">...</div>
<div id="ck-unique-div">...</div>
<div id="ck-a-very-long-unique-identifier">...</div>
# 文件名
文件和目录名称必须遵循标准,使它们的语法易于预测
- 全部小写。
- 只接受字母数字字符。
- 单词用破折号 (
-
) 分隔 (kebab-case)。- 代码实体被视为单个单词,因此
DataProcessor
类在dataprocessor.js
文件中定义。 - 但是,一个测试文件涵盖了“多根编辑器中的突变”:
mutations-in-multi-root-editors.js
。
- 代码实体被视为单个单词,因此
- HTML 文件具有
.html
扩展名。
# 示例
ckeditor.js
tools.js
editor.js
dataprocessor.js
build-all.js
和build-min.js
test-core-style-system.html
# 标准文件
广泛使用的标准文件不遵守上述规则
README.md
、LICENSE.md
、CONTRIBUTING.md
、CHANGES.md
.gitignore
和所有标准的“点文件”node_modules
# CKEditor 5 自定义 ESLint 规则
除了 ESLint 提供的规则外,CKEditor 5 还使用以下描述的一些自定义规则。
# 在包之间导入:ckeditor5-rules/no-relative-imports
从同一个包导入模块时,可以使用相对路径,例如
// Assume we edit a file located in the path: `packages/ckeditor5-engine/src/model/model.js`
import Position from './position';
import insertContent from './utils/insertcontent';
从其他包导入模块时,不允许使用相对路径,并且必须使用包名进行导入,例如
👎 此规则不正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-engine/src/model/model.js`
import CKEditorError from '../../../ckeditor5-utils/src/ckeditorerror';
即使导入语句在本地有效,它也会在开发人员从 npm 安装包时引发错误。
👍 此规则正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-engine/src/model/model.js`
import { CKEditorError } from 'ckeditor5';
# 错误的描述:ckeditor5-rules/ckeditor-error-message
每次创建新的错误时,都需要一个描述以在 错误代码 页面上显示,例如
👎 此规则不正确代码的示例
// Missing the error's description.
throw new CKEditorError( 'ckeditor5-example-error', this );
// ESLint shouldn't expect the definition of the error as it is already described.
throw new CKEditorError( 'editor-wrong-element', this );
👍 此规则正确代码的示例
// This error occurs for the first time in the project, so it needs to be defined.
/**
* Description of why the error was thrown and how to fix the code.
*
* @error ckeditor5-example-error
*/
throw new CKEditorError( 'ckeditor5-example-error', this );
// This error is already described, so we don't need to provide its documentation.
// We need to disable ESLint for checking the rule.
// It is a good practice to include a note that explains where it is described.
// Documented in core/editor/editor.js
// eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
throw new CKEditorError( 'editor-wrong-element', this );
# DLL 构建:ckeditor5-rules/ckeditor-imports
为了使 CKEditor 5 插件彼此兼容,我们需要在从包中导入文件时引入限制。
标记为“Base DLL 构建”的包可以彼此之间无限制地导入。这些包的名称在 DLL 构建 指南中指定。
其他 CKEditor 5 功能(非 DLL)可以使用 ckeditor5
包导入“Base DLL”包。
从 ckeditor5
包导入模块时,所有导入都必须来自 src/
目录。其他目录不会发布到 npm,因此此类导入将无法工作。
👎 此规则不正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-basic-styles/src/bold.js`
import { Plugin } from '@ckeditor/ckeditor5-core';
// The import uses the `ckeditor5` package, but the specified path does not exist when installing the package from npm.
import { Plugin } from 'ckeditor5/packages/ckeditor5-core';
👍 此规则正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-basic-styles/src/bold.js`
import { Plugin } from 'ckeditor5/src/core';
此外,非 DLL 包不应该在非 DLL 包之间导入,以避免在构建 DLL 构建时出现代码重复。
👎 此规则不正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-link/src/linkimage.js`
import { createImageViewElement } from '@ckeditor/ckeditor5-image'
要使用 createImageViewElement()
函数,请考虑实现一个实用程序插件,该插件将在 ckeditor5-image
包中公开所需函数。
从另一个 DLL 包导入 DLL 包时,导入语句必须使用导入包的完整名称,而不是使用 ckeditor5
符号。
👎 此规则不正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-widget/src/widget.js`
import { Plugin } from 'ckeditor5/src/core';
👍 此规则正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-widget/src/widget.js`
import { Plugin } from 'ckeditor5';
更改历史记录
#跨包导入:ckeditor5-rules/no-cross-package-imports
允许从其他包导入模块
import { toArray } from 'ckeditor5/src/utils';
但是,某些包无法从 CKEditor 5 导入模块,因为这会导致代码重复和运行时错误。因此,该规则禁用此类导入。
目前,它适用于 @ckeditor/ckeditor5-watchdog
包。
👎 此规则不正确代码的示例
// Assume we edit a file located in the `packages/ckeditor5-watchdog/` directory.
import { toArray } from 'ckeditor5/src/utils';
import { toArray } from 'ckeditor5';
#在调试注释中导入模块:ckeditor5-rules/use-require-for-debug-mode-imports
调试模式允许导入其他模块以进行测试。不幸的是,调试注释不会被删除,因此 webpack 会报告以下错误。
Module parse failed: 'import' and 'export' may only appear at the top level (15204:20)
File was processed with these loaders:
* ./node_modules/@ckeditor/ckeditor5-dev-tests/lib/utils/ck-debug-loader.js
You may need an additional loader to handle the result of these loaders.
| */
|
> /* @if CK_DEBUG */ import { CKEditorError } from 'ckeditor5/src/utils';
|
| /**
模块需要使用 require()
函数导入。
要创建仅在调试模式下执行的代码,请遵循 测试环境 指南中 --debug
标志的说明。
👎 此规则不正确代码的示例
// @if CK_DEBUG // import defaultExport from 'module-name';
// @if CK_DEBUG // import * as name from 'module-name';
// @if CK_DEBUG // import { testFunction } from 'module-name';
// @if CK_DEBUG // import { default as alias } from 'module-name';
// @if CK_DEBUG // import { exported as alias } from 'module-name';
// @if CK_DEBUG // import 'module-name';
👍 此规则正确代码的示例
// @if CK_DEBUG // const defaultExport = require( 'module-name' ).default;
// @if CK_DEBUG // const name = require( 'module-name' );
// @if CK_DEBUG // const { testFunction } = require( 'module-name' );
// @if CK_DEBUG // const alias = require( 'module-name' ).default;
// @if CK_DEBUG // const { exported: alias } = require( 'module-name' );
// @if CK_DEBUG // require( 'module-name' );
#标记为 @internal 的非公共成员:ckeditor5-rules/non-public-members-as-internal
此规则应仅用于 .ts
文件。
要从类型定义中删除非公共成员,请在成员的 JSDoc 中使用 @internal
标签。
然后,您可以在 tsconfig.json
中使用 "stripInternal": true
选项将其从声明类型中删除。
👎 此规则不正确代码的示例
class ClassWithSecrets {
private _shouldNotBeEmitted: string;
}
👍 此规则正确代码的示例
class ClassWithSecrets {
/**
* @internal
*/
private _shouldNotBeEmitted: string;
}
#导入预定义的构建:ckeditor5-rules/no-build-extensions
此规则仅适用于文档中的代码片段。
在导入预定义的构建时,只允许导入此构建,如下所示
// Assume we edit a file located in the path: `packages/ckeditor5-alignment/docs/_snippets/features/text-alignment.js`.
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
不允许从 src
目录导入任何内容以扩展 CKEditor 5 构建。预定义构建中的其他目录不会发布到 npm,因此此类导入将不起作用。
👎 此规则不正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-alignment/docs/_snippets/features/text-alignment.js`.
import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
// Assume we edit a file located in the path: `docs/_snippets/features/placeholder.js`.
import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
#为核心包声明模块增强:ckeditor5-rules/allow-declare-module-only-in-augmentation-file
此规则应仅用于 .ts
文件。
大多数模块中的主要入口点(index.ts
文件)都有一个副作用导入 import './augmentation'
,该导入使用模块增强来使用有关可用插件、配置和命令的信息填充编辑器类型。
此规则强制所有 declare module '@ckeditor/ckeditor5-core'
在此 augmentation.ts
文件中定义。
#从模块导入:ckeditor5-rules/allow-imports-only-from-main-package-entry-point
此规则确保所有来自 @ckeditor/*
包的导入都是通过主要包入口点完成的。这是编辑器类型正常工作并简化迁移到 CKEditor 5 v42.0.0 中引入的安装方法所必需的。
👎 此规则的错误代码示例
// Importing from the `/src/` folder is not allowed.
import Table from '@ckeditor/ckeditor5-table/src/table';
// Importing from the `/theme/` folder is not allowed.
import BoldIcon from '@ckeditor/ckeditor5-core/theme/icons/bold.svg';
👍 此规则正确代码的示例
// ✔️ Importing from the main entry point is allowed.
import { Table } from 'ckeditor5';
#需要 as const
:ckeditor5-rules/require-as-const-returns-in-methods
此规则应仅用于 .ts
文件。
在 TypeScript 中,从某些值推断出的类型被简化了。例如,const test = [1, 2, 3];
的类型是 number[]
,但在某些情况下可能需要更具体的类型。使用 as const
可以帮助解决这个问题。例如,const test1 = [1, 2, 3] as const;
的类型是 readonly [1, 2, 3]
。
require-as-const-returns-in-methods
规则要求某些依赖于返回数据的精确类型的方法(例如,pluginName
方法中的泛型 string
而不是 'delete'
文字字符串,或者 requires
方法中的 []
而不是 readonly [typeof Table]
)具有所有带有 as const
的 return 语句。
👎 此规则不正确代码的示例
export default class Delete extends Plugin {
public static get pluginName(): string {
return 'Delete';
}
}
export default class Delete extends Plugin {
public static get pluginName(): 'Delete' {
return 'Delete';
}
}
👍 此规则正确代码的示例
export default class Delete extends Plugin {
public static get pluginName() {
return 'Delete' as const;
}
}
#包内的导入:ckeditor5-rules/no-scoped-imports-within-package
在每个包中定义的所有导入,指向同一个包中的文件,都必须是相对的。如果目标文件与导入声明位于同一个包中,则不能使用范围导入。解析后的范围导入指向 node_modules
中的包,而不是当前工作目录,这两个位置的源代码可能彼此不同。
👎 此规则不正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-alignment/src/alignment.ts`.
// Both imports are incorrect.
import { AlignmentEditing } from '@ckeditor/ckeditor5-alignment';
import AlignmentEditing from '@ckeditor/ckeditor5-alignment/src/alignmentediting';
👍 此规则正确代码的示例
// Assume we edit a file located in the path: `packages/ckeditor5-alignment/src/alignment.ts`.
import AlignmentEditing from './alignmentediting';
#导入中强制使用文件扩展名:ckeditor5-rules/require-file-extensions-in-imports
根据 ECMAScript (ESM) 标准的要求,所有导入和重新导出都必须包含文件扩展名。如果它们不包含它,此规则将尝试自动检测正确的文件扩展名。在以下两种情况下这是不可能的
- 文件扩展名与
.ts
、.js
或.json
不同。 - 文件在文件系统中不存在。
第二种情况在文档文件中很常见,因为它的部分位于不同的目录和存储库中。这些部分在构建步骤期间合并,但在合并之前,导入在技术上是无效的。
在这种情况下,您必须手动添加文件扩展名。带有文件扩展名的导入不会被验证。
#要求或禁止某些插件标志:ckeditor5-rules/ckeditor-plugin-flags
此规则应仅用于 .ts
文件。
此规则确保插件标志正确设置或根本没有设置。它检查标志是否具有正确的类型和值,防止常见错误并确保符合 CKEditor 5 插件 API。
选项
requiredFlags
– (可选)必须在插件中设置的标志数组。disallowedFlags
– (可选)必须在插件中未设置的标志数组。
下面的示例配置要求将 isFooPlugin
标志设置为 true
,并且禁止 isBarPlugin
标志
{
"requiredFlags": [
{
"name": "isFooPlugin",
"returnValue": true
}
],
"disallowedFlags": [ "isBarPlugin" ]
}
👎 此规则不正确代码的示例
export default class MyPlugin extends Plugin {
static get pluginName() {
return 'MyPlugin';
}
public static override get isBarPlugin(): false {
return false;
}
}
isBarPlugin
标志被禁止,插件将其设置为 false
。此外,isFooPlugin
标志是必需的,但未定义。
👍 此规则正确代码的示例
export default class MyPlugin extends Plugin {
static get pluginName() {
return 'MyPlugin';
}
public static override get isFooPlugin(): true {
return true;
}
}
isFooPlugin
标志是必需的,并设置为 true
,isBarPlugin
标志未定义。
#无遗留导入
此规则确保使用 新的安装方法 进行导入。所有导入都应使用 ckeditor5
包来获取编辑器核心和所有开源插件,或者使用 ckeditor5-premium-features
来获取高级功能。
👎 此规则不正确代码的示例
// Import from `ckeditor5/src/*`.
import { Plugin } from 'ckeditor5/src/core.js';
// Import from individual open-source package.
import { Plugin } from '@ckeditor/ckeditor5-core';
// Import from individual premium package.
import { AIAssistant } from '@ckeditor/ckeditor5-ai';
👍 此规则正确代码的示例
import { Plugin } from 'ckeditor5';
import { AIAssistant } from 'ckeditor5-premium-features';
我们每天都在努力使我们的文档完整。您是否发现过时信息?是否缺少什么?请通过我们的 问题跟踪器 报告。
随着 42.0.0 版本的发布,我们重写了我们的许多文档以反映新的导入路径和功能。我们感谢您的反馈,以帮助我们确保其准确性和完整性。