Commit 4a6fd516 authored by David Sveningsson's avatar David Sveningsson

feat(transform): support chaining transformers

parent 152b4df5
......@@ -5,6 +5,42 @@
# Transformers
## API
Each transformer must implement the following API:
```typescript
import { Transformer, TransformContext } from "html-validate";
module.exports = function(
this: TransformContext,
source: Source
): Source[] {
/* ... */
};
```
### `TransformContext`
```typescript
export interface TransformContext {
chain(source: Source, filename: string): Source[];
}
```
#### `chain`
Chain transformations. Sometimes multiple transformers must be applied. For
instance, a Markdown file with JSX in a code-block.
```typescript
const source = myTransformation();
return this.chain(source, `${originalFilename}.foo`);
```
The above snippet will chain transformations using the current transformer
matching `*.foo` files, if it is configured.
## `TemplateExtractor`
Extracts templates from javascript sources.
......
......@@ -378,6 +378,28 @@ describe("config", () => {
`);
});
it("should support chaining transformer", () => {
const config = Config.fromObject({
transform: {
"^.*\\.bar$": "mock-transform-chain-foo",
"^.*\\.foo$": "mock-transform",
},
});
config.init();
source.filename = "/path/to/test.bar";
expect(config.transformSource(source)).toMatchInlineSnapshot(`
Array [
Object {
"column": 1,
"data": "transformed source (was: data from mock-transform-chain-foo (was: original data))",
"filename": "/path/to/test.bar",
"line": 1,
"originalData": "original data",
},
]
`);
});
it("should replace <rootDir>", () => {
const config = Config.fromObject({
transform: {
......
......@@ -327,11 +327,17 @@ export class Config {
* When transforming zero or more new sources will be generated.
*
* @param source - Current source to transform.
* @param filename - If set it is the filename used to match
* transformer. Default is to use filename from source.
* @return A list of transformed sources ready for validation.
*/
public transformSource(source: Source): Source[] {
const transformer = this.findTransformer(source.filename);
const context: TransformContext = {};
public transformSource(source: Source, filename?: string): Source[] {
const transformer = this.findTransformer(filename || source.filename);
const context: TransformContext = {
chain: (source: Source, filename: string) => {
return this.transformSource(source, filename);
},
};
if (transformer) {
try {
return transformer.fn.call(context, source);
......
import { Source } from "../../context";
import { Transformer, TransformContext } from "..";
/**
* Mock transformer chaining to a .foo file transformer.
*/
module.exports = function mockTransformChainFoo(
this: TransformContext,
source: Source
) {
return this.chain(
{
data: `data from mock-transform-chain-foo (was: ${source.data})`,
filename: source.filename,
line: 1,
column: 1,
originalData: source.originalData || source.data,
},
`${source.filename}.foo`
);
} as Transformer;
/* eslint-disable-next-line @typescript-eslint/no-empty-interface */
export interface TransformContext {}
import { Source } from "../context";
export interface TransformContext {
/**
* Chain transformations.
*
* Sometimes multiple transformers must be applied. For instance, a Markdown
* file with JSX in a code-block.
*
* @param source - Source to chain transformations on.
* @param filename - Filename to use to match next transformer (unrelated to
* filename set in source)
*/
chain(source: Source, filename: string): Source[];
}
Markdown is supported
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