Commit 4c1d1155 authored by David Sveningsson's avatar David Sveningsson
Browse files

feat: add new event `source:ready`

fixes #115
parent 06cb7079
---
docType: content
title: API - Source
id: api:Source
name: Source
---
# `Source`
Source interface.
HTML source with file, line and column context.
Optional hooks can be attached.
This is usually added by transformers to postprocess.
```ts
interface Source {
data: string;
filename: string;
/**
* Line in the original data.
*
* Starts at 1 (first line).
*/
line: number;
/**
* Column in the original data.
*
* Starts at 1 (first column).
*/
column: number;
/**
* Offset in the original data.
*
* Starts at 0 (first character).
*/
offset: number;
/**
* Original data. When a transformer extracts a portion of the original source
* this must be set to the full original source.
*
* Since the transformer might be chained always test if the input source
* itself has `originalData` set, e.g.:
*
* `originalData = input.originalData || input.data`.
*/
originalData?: string;
/**
* Hooks for processing the source as it is being parsed.
*/
hooks?: SourceHooks;
/**
* Internal property to keep track of what transformers has run on this
* source. Entries are in reverse-order, e.g. the last applied transform is
* first.
*/
transformedBy?: string[];
}
```
## `SourceHooks`
```
interface SourceHooks {
/**
* Called for every attribute.
*
* The original attribute must be yielded as well or no attribute will be
* added.
*
* @returns Attribute data for an attribute to be added to the element.
*/
processAttribute?: ProcessAttributeCallback | null;
/**
* Called for every element after element is created but before any children.
*
* May modify the element.
*/
processElement?: ProcessElementCallback | null;
}
```
......@@ -18,6 +18,20 @@ title: Events
Emitted after after configuration is ready but before DOM is initialized.
### `source:ready`
```typescript
{
source: Source;
}
```
Emitted after after source is transformed but before DOM is initialized.
See {@link api:Source} for data structure.
The source object must not be modified (use a transformer if modifications are required).
The `hooks` property is always unset.
### `token`
```typescript
......
......@@ -196,6 +196,49 @@ describe("Engine", () => {
expect(event.config).toEqual(config.get());
expect(event.rules).toBeDefined();
});
it("should generate source:ready event", () => {
expect.assertions(3);
const source: Source[] = [inline("<div></div>"), inline("<p></i>")];
const parser = new Parser(config.resolve());
const spy = jest.fn();
parser.on("source:ready", spy);
jest.spyOn(engine, "instantiateParser").mockReturnValue(parser);
engine.lint(source);
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith("source:ready", {
location: {
filename: "inline",
line: 1,
column: 1,
offset: 0,
size: 1,
},
source: {
filename: "inline",
data: "<div></div>",
line: 1,
column: 1,
offset: 0,
},
});
expect(spy).toHaveBeenCalledWith("source:ready", {
location: {
filename: "inline",
line: 1,
column: 1,
offset: 0,
size: 1,
},
source: {
filename: "inline",
data: "<p></i>",
line: 1,
column: 1,
offset: 0,
},
});
});
});
describe("directive", () => {
......
import { ConfigData, ResolvedConfig, RuleOptions, Severity } from "../config";
import { Location, Source } from "../context";
import { HtmlElement } from "../dom";
import { ConfigReadyEvent, DirectiveEvent, TagEndEvent, TagStartEvent } from "../event";
import {
ConfigReadyEvent,
DirectiveEvent,
SourceReadyEvent,
TagEndEvent,
TagStartEvent,
} from "../event";
import { InvalidTokenError, Lexer, TokenType } from "../lexer";
import { Parser, ParserError } from "../parser";
import { Report, Reporter } from "../reporter";
......@@ -58,19 +64,31 @@ export class Engine<T extends Parser = Parser> {
/* setup plugins and rules */
const { rules } = this.setupPlugins(source, this.config, parser);
/* create a faux location at the start of the stream for the next events */
const location: Location = {
filename: source.filename,
line: 1,
column: 1,
offset: 0,
size: 1,
};
/* trigger configuration ready event */
const event: ConfigReadyEvent = {
location: {
filename: source.filename,
line: 1,
column: 1,
offset: 0,
size: 1,
},
const configEvent: ConfigReadyEvent = {
location,
config: this.configData,
rules,
};
parser.trigger("config:ready", event);
parser.trigger("config:ready", configEvent);
/* trigger source ready event */
/* eslint-disable-next-line @typescript-eslint/no-unused-vars -- object destructured on purpose to remove property */
const { hooks: _, ...sourceData } = source;
const sourceEvent: SourceReadyEvent = {
location,
source: sourceData,
};
parser.trigger("source:ready", sourceEvent);
/* setup directive handling */
parser.on("directive", (_: string, event: DirectiveEvent) => {
......
import { ConfigData } from "../config";
import { Location } from "../context";
import { Location, Source } from "../context";
import { DOMTree, DynamicValue, HtmlElement } from "../dom";
import { TokenType } from "../lexer";
import { Rule } from "../rule";
......@@ -20,6 +20,17 @@ export interface ConfigReadyEvent extends Event {
rules: { [ruleId: string]: Rule };
}
/**
* Source ready event. Emitted after source has been transformed but before any
* markup is processed.
*
* The source object must not be modified (use a transformer if modifications
* are required)
*/
export interface SourceReadyEvent extends Event {
source: Source;
}
/**
* Token event.
*/
......@@ -181,6 +192,7 @@ export interface DOMReadyEvent extends Event {
export interface TriggerEventMap {
"config:ready": ConfigReadyEvent;
"source:ready": SourceReadyEvent;
token: TokenEvent;
"tag:start": TagStartEvent;
"tag:end": TagEndEvent;
......@@ -197,6 +209,7 @@ export interface TriggerEventMap {
export interface ListenEventMap {
"config:ready": ConfigReadyEvent;
"source:ready": SourceReadyEvent;
token: TokenEvent;
"tag:open": TagOpenEvent;
"tag:start": TagStartEvent;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment