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

Ck/11724 ts engine #12188

Merged
merged 30 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
164f369
Merge branch 'ck/epic/11708-typescript-first-release' into ck/epic/11…
arkflpc Jul 15, 2022
6ffd605
Merge branch 'master' into ck/epic/11708-migrate-typescript-mvp
arkflpc Jul 28, 2022
a06baa5
Merge remote-tracking branch 'origin/master' into ck/epic/11708-migra…
arkflpc Jul 29, 2022
c9d6964
Engine TypeScript Work in Progress
arkflpc Jul 29, 2022
1d6891d
WiP.
niegowski Jul 29, 2022
946877e
Revert "Better BR elements handling"
niegowski Jul 29, 2022
2acbcb3
Fixed premature iterator consumption.
niegowski Jul 29, 2022
e5982db
Fixed empty element children handling.
niegowski Jul 29, 2022
083ffab
Merge branch 'master' into engine-wip
arkflpc Aug 1, 2022
e6ca30e
Fix tsconfig.json
arkflpc Aug 1, 2022
7e77ea1
Added packages/ckeditor5-engine/tsconfig.release.json
arkflpc Aug 1, 2022
5bd94bf
Fix ckeditor5-engine/package.json
arkflpc Aug 1, 2022
9d81d2b
Added missing 'selectionChange' event type.
niegowski Aug 1, 2022
b996d3a
Added conversion events prefixes.
niegowski Aug 1, 2022
8257258
TS: bringing code coverage back to 100%
arkflpc Aug 2, 2022
f89b68b
Review fixes.
niegowski Aug 2, 2022
99be45b
TS: Add typed events for observers
arkflpc Aug 2, 2022
b9213fd
Merge branch 'ck/11724-ts-engine' of github.com:ckeditor/ckeditor5 in…
arkflpc Aug 2, 2022
448971b
TS: mix changes
arkflpc Aug 2, 2022
26fde8b
Review fixes.
niegowski Aug 2, 2022
d62005d
Review fixes.
niegowski Aug 2, 2022
d8cd2e1
Review fixes.
niegowski Aug 2, 2022
69983fe
Review fixes.
niegowski Aug 2, 2022
fc7eb06
Narrowed parameters for DOM-View mapping.
niegowski Aug 2, 2022
7229d0c
Fixed code indentation.
niegowski Aug 2, 2022
c3f7c57
Fixed code indentation.
niegowski Aug 2, 2022
411da53
Fixed code indentation.
niegowski Aug 3, 2022
2fcbd83
Merge branch 'master' into ck/11724-ts-engine
arkflpc Aug 4, 2022
f2e7b49
TS: engine - add comment about unreachable code
arkflpc Aug 4, 2022
3541eef
TS: engine - add previous *.js files as _src
arkflpc Aug 4, 2022
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
12 changes: 9 additions & 3 deletions packages/ckeditor5-engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"ckeditor5-lib",
"ckeditor5-dll"
],
"main": "src/index.js",
"main": "src/index.ts",
"dependencies": {
"@ckeditor/ckeditor5-utils": "^34.2.0",
"lodash-es": "^4.17.15"
Expand All @@ -47,6 +47,7 @@
"@ckeditor/ckeditor5-ui": "^34.2.0",
"@ckeditor/ckeditor5-undo": "^34.2.0",
"@ckeditor/ckeditor5-widget": "^34.2.0",
"typescript": "^4.6.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
},
Expand All @@ -65,9 +66,14 @@
},
"files": [
"lang",
"src",
"src/**/*.js",
"src/**/*.d.ts",
"theme",
"ckeditor5-metadata.json",
"CHANGELOG.md"
]
],
"scripts": {
"build": "tsc -p ./tsconfig.release.json",
"postversion": "npm run build"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,41 @@
* @module engine/controller/datacontroller
*/

import mix from '@ckeditor/ckeditor5-utils/src/mix';
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
import { Observable } from '@ckeditor/ckeditor5-utils/src/observablemixin';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import { Emitter } from '@ckeditor/ckeditor5-utils/src/emittermixin';

import Mapper from '../conversion/mapper';

import DowncastDispatcher from '../conversion/downcastdispatcher';
import DowncastDispatcher, { type DowncastInsertEvent } from '../conversion/downcastdispatcher';
import { insertAttributesAndChildren, insertText } from '../conversion/downcasthelpers';

import UpcastDispatcher from '../conversion/upcastdispatcher';
import UpcastDispatcher, {
type UpcastDocumentFragmentEvent,
type UpcastElementEvent,
type UpcastTextEvent
} from '../conversion/upcastdispatcher';
import { convertText, convertToModelFragment } from '../conversion/upcasthelpers';

import ViewDocumentFragment from '../view/documentfragment';
import ViewDocument from '../view/document';
import ViewDowncastWriter from '../view/downcastwriter';
import type ViewElement from '../view/element';
import { type StylesProcessor } from '../view/stylesmap';
import { type MatcherPattern } from '../view/matcher';

import ModelRange from '../model/range';
import type Model from '../model/model';
import type ModelText from '../model/text';
import type ModelElement from '../model/element';
import type ModelTextProxy from '../model/textproxy';
import type ModelDocumentFragment from '../model/documentfragment';
import { type SchemaContextDefinition } from '../model/schema';
import { type BatchType } from '../model/batch';
import { autoParagraphEmptyRoots } from '../model/utils/autoparagraphing';

import HtmlDataProcessor from '../dataprocessor/htmldataprocessor';
import type DataProcessor from '../dataprocessor/dataprocessor';

/**
* Controller for the data pipeline. The data pipeline controls how data is retrieved from the document
Expand All @@ -42,16 +58,29 @@ import HtmlDataProcessor from '../dataprocessor/htmldataprocessor';
*
* editor.data.get( { rootName: 'customRoot' } ); // -> '<p>Hello!</p>'
*
* @mixes module:utils/observablemixin~ObservableMixin
* @mixes module:utils/emittermixin~EmitterMixin
*/
export default class DataController {
export default class DataController extends Emitter {
public readonly model: Model;
public readonly mapper: Mapper;
public readonly downcastDispatcher: DowncastDispatcher;
public readonly upcastDispatcher: UpcastDispatcher;
public readonly viewDocument: ViewDocument;
public readonly stylesProcessor: StylesProcessor;
public readonly htmlProcessor: HtmlDataProcessor;
public processor: DataProcessor;

private readonly _viewWriter: ViewDowncastWriter;

/**
* Creates a data controller instance.
*
* @param {module:engine/model/model~Model} model Data model.
* @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
*/
constructor( model, stylesProcessor ) {
constructor( model: Model, stylesProcessor: StylesProcessor ) {
super();

/**
* Data model.
*
Expand Down Expand Up @@ -80,8 +109,8 @@ export default class DataController {
mapper: this.mapper,
schema: model.schema
} );
this.downcastDispatcher.on( 'insert:$text', insertText(), { priority: 'lowest' } );
this.downcastDispatcher.on( 'insert', insertAttributesAndChildren(), { priority: 'lowest' } );
this.downcastDispatcher.on<DowncastInsertEvent<ModelText | ModelTextProxy>>( 'insert:$text', insertText(), { priority: 'lowest' } );
this.downcastDispatcher.on<DowncastInsertEvent>( 'insert', insertAttributesAndChildren(), { priority: 'lowest' } );

/**
* Upcast dispatcher used by the {@link #set set method}. Upcast converters should be attached to it.
Expand Down Expand Up @@ -140,13 +169,13 @@ export default class DataController {
// Note that if there is no default converter for the element it will be skipped, for instance `<b>foo</b>` will be
// converted to nothing. We therefore add `convertToModelFragment` as a last converter so it converts children of that
// element to the document fragment so `<b>foo</b>` will still be converted to `foo` even if there is no converter for `<b>`.
this.upcastDispatcher.on( 'text', convertText(), { priority: 'lowest' } );
this.upcastDispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
this.upcastDispatcher.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } );
this.upcastDispatcher.on<UpcastTextEvent>( 'text', convertText(), { priority: 'lowest' } );
this.upcastDispatcher.on<UpcastElementEvent>( 'element', convertToModelFragment(), { priority: 'lowest' } );
this.upcastDispatcher.on<UpcastDocumentFragmentEvent>( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } );

this.decorate( 'init' );
this.decorate( 'set' );
this.decorate( 'get' );
Observable.prototype.decorate.call( this, 'init' as any );
Observable.prototype.decorate.call( this, 'set' as any );
Observable.prototype.decorate.call( this, 'get' as any );

// Fire the `ready` event when the initialization has completed. Such low-level listener offers the possibility
// to plug into the initialization pipeline without interrupting the initialization flow.
Expand Down Expand Up @@ -174,8 +203,8 @@ export default class DataController {
* use `'none'`. In such cases the exact content will be returned (for example a `<p>&nbsp;</p>` for an empty editor).
* @returns {String} Output data.
*/
get( options = {} ) {
const { rootName = 'main', trim = 'empty' } = options;
public get( options: Record<string, unknown> = {} ): string {
const { rootName = 'main', trim = 'empty' } = options as Record<string, string>;

if ( !this._checkIfRootsExists( [ rootName ] ) ) {
/**
Expand All @@ -192,7 +221,7 @@ export default class DataController {
throw new CKEditorError( 'datacontroller-get-non-existent-root', this );
}

const root = this.model.document.getRoot( rootName );
const root = this.model.document.getRoot( rootName )!;

if ( trim === 'empty' && !this.model.hasContent( root, { ignoreWhitespaces: true } ) ) {
return '';
Expand All @@ -211,7 +240,10 @@ export default class DataController {
* @param {Object} [options] Additional configuration passed to the conversion process.
* @returns {String} Output data.
*/
stringify( modelElementOrFragment, options = {} ) {
public stringify(
modelElementOrFragment: ModelElement | ModelDocumentFragment,
options: Record<string, unknown> = {}
): string {
// Model -> view.
const viewDocumentFragment = this.toView( modelElementOrFragment, options );

Expand All @@ -231,7 +263,10 @@ export default class DataController {
* {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi#options} during the conversion process.
* @returns {module:engine/view/documentfragment~DocumentFragment} Output view DocumentFragment.
*/
toView( modelElementOrFragment, options = {} ) {
public toView(
modelElementOrFragment: ModelElement | ModelDocumentFragment,
options: Record<string, unknown> = {}
): ViewDocumentFragment {
const viewDocument = this.viewDocument;
const viewWriter = this._viewWriter;

Expand Down Expand Up @@ -279,7 +314,7 @@ export default class DataController {
* pairs to initialize data on multiple roots at once.
* @returns {Promise} Promise that is resolved after the data is set on the editor.
*/
init( data ) {
public init( data: string | Record<string, string> ): Promise<void> {
if ( this.model.document.version ) {
/**
* Cannot set initial data to a non-empty {@link module:engine/model/document~Document}.
Expand All @@ -291,7 +326,8 @@ export default class DataController {
throw new CKEditorError( 'datacontroller-init-document-not-empty', this );
}

let initialData = {};
let initialData: Record<string, string> = {};

if ( typeof data === 'string' ) {
initialData.main = data; // Default root is 'main'. To initiate data on a different root, object should be passed.
} else {
Expand All @@ -315,7 +351,8 @@ export default class DataController {

this.model.enqueueChange( { isUndoable: false }, writer => {
for ( const rootName of Object.keys( initialData ) ) {
const modelRoot = this.model.document.getRoot( rootName );
const modelRoot = this.model.document.getRoot( rootName )!;

writer.insert( this.parse( initialData[ rootName ], modelRoot ), modelRoot, 0 );
}
} );
Expand Down Expand Up @@ -353,8 +390,8 @@ export default class DataController {
* cleared after the new data is applied (all undo steps will be removed). If the batch type `isUndoable` flag is be set to `true`,
* the undo stack will be preserved instead and not cleared when new data is applied.
*/
set( data, options = {} ) {
let newData = {};
public set( data: string | Record<string, string>, options: { batchType?: BatchType } = {} ): void {
let newData: Record<string, string> = {};

if ( typeof data === 'string' ) {
newData.main = data; // The default root is 'main'. To set data on a different root, an object should be passed.
Expand Down Expand Up @@ -383,7 +420,7 @@ export default class DataController {

for ( const rootName of Object.keys( newData ) ) {
// Save to model.
const modelRoot = this.model.document.getRoot( rootName );
const modelRoot = this.model.document.getRoot( rootName )!;

writer.remove( writer.createRangeIn( modelRoot ) );
writer.insert( this.parse( newData[ rootName ], modelRoot ), modelRoot, 0 );
Expand All @@ -401,7 +438,7 @@ export default class DataController {
* be converted to the model. See: {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#convert}.
* @returns {module:engine/model/documentfragment~DocumentFragment} Parsed data.
*/
parse( data, context = '$root' ) {
public parse( data: string, context: SchemaContextDefinition = '$root' ): ModelDocumentFragment {
// data -> view
const viewDocumentFragment = this.processor.toView( data );

Expand All @@ -423,7 +460,10 @@ export default class DataController {
* be converted to the model. See: {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#convert}.
* @returns {module:engine/model/documentfragment~DocumentFragment} Output document fragment.
*/
toModel( viewElementOrFragment, context = '$root' ) {
public toModel(
viewElementOrFragment: ViewElement | ViewDocumentFragment,
context: SchemaContextDefinition = '$root'
): ModelDocumentFragment {
return this.model.change( writer => {
return this.upcastDispatcher.convert( viewElementOrFragment, writer, context );
} );
Expand All @@ -441,7 +481,7 @@ export default class DataController {
*
* @param {Function} callback
*/
addStyleProcessorRules( callback ) {
public addStyleProcessorRules( callback: ( stylesProcessor: StylesProcessor ) => void ): void {
callback( this.stylesProcessor );
}

Expand All @@ -456,7 +496,7 @@ export default class DataController {
* @param {module:engine/view/matcher~MatcherPattern} pattern Pattern matching all view elements whose content should
* be treated as a raw data.
*/
registerRawContentMatcher( pattern ) {
public registerRawContentMatcher( pattern: MatcherPattern ): void {
// No need to register the pattern if both the `htmlProcessor` and `processor` are the same instances.
if ( this.processor && this.processor !== this.htmlProcessor ) {
this.processor.registerRawContentMatcher( pattern );
Expand All @@ -468,7 +508,7 @@ export default class DataController {
/**
* Removes all event listeners set by the DataController.
*/
destroy() {
public destroy(): void {
this.stopListening();
}

Expand All @@ -479,7 +519,7 @@ export default class DataController {
* @param {Array.<String>} rootNames Root names to check.
* @returns {Boolean} Whether all provided root names are existing editor roots.
*/
_checkIfRootsExists( rootNames ) {
private _checkIfRootsExists( rootNames: string[] ): boolean {
for ( const rootName of rootNames ) {
if ( !this.model.document.getRootNames().includes( rootName ) ) {
return false;
Expand Down Expand Up @@ -525,15 +565,13 @@ export default class DataController {
*/
}

mix( DataController, ObservableMixin );

// Helper function for downcast conversion.
//
// Takes a document element (element that is added to a model document) and checks which markers are inside it. If the marker is collapsed
// at element boundary, it is considered as contained inside the element and marker range is returned. Otherwise, if the marker is
// intersecting with the element, the intersection is returned.
function _getMarkersRelativeToElement( element ) {
const result = [];
function _getMarkersRelativeToElement( element: ModelElement ): Map<string, ModelRange> {
const result: [ string, ModelRange ][] = [];
const doc = element.root.document;

if ( !doc ) {
Expand Down
Loading