...
 
Commits (7)
# html-validate changelog
# [2.3.0](https://gitlab.com/html-validate/html-validate/compare/v2.2.0...v2.3.0) (2019-11-27)
### Bug Fixes
- **config:** update `--init` config for [email protected] ([6553ded](https://gitlab.com/html-validate/html-validate/commit/6553ded78cf8cd51c8eec9ba2ef08f8e25e84612))
### Features
- **transform:** support `hasChain` to test if a transformer is present ([e8ef4f5](https://gitlab.com/html-validate/html-validate/commit/e8ef4f5e1f89c70bad43cbf5d04f47789080ab4e))
# [2.2.0](https://gitlab.com/html-validate/html-validate/compare/v2.1.0...v2.2.0) (2019-11-23)
### Bug Fixes
......
......@@ -28,14 +28,15 @@ module.exports = myTransform as Transfomer;
```typescript
export interface TransformContext {
hasChain(filename: string): boolean;
chain(source: Source, filename: string): Iterable<Source>;
}
```
#### `chain`
Chain transformations. Sometimes multiple transformers must be applied. For
instance, a Markdown file with JSX in a code-block.
Chain transformations. Sometimes multiple transformers must be applied.
For instance, a Markdown file with JSX in a code-block.
```typescript
for (const source of myTransformation()) {
......@@ -43,8 +44,39 @@ for (const source of myTransformation()) {
}
```
The above snippet will chain transformations using the current transformer
matching `*.foo` files, if it is configured.
The above snippet will chain transformations using the current transformer matching `*.foo` files, if it is configured.
If no pattern matches the filename the input source is returned unmodified.
Use `hasChain` to test if chaining is present.
#### `hasChain`
Test if an additional chainable transformer is present.
Returns true only if there is a transformer configured for the given filename.
While it is always safe to call `chain(..)` as it will passthru sources without a chainable transform it is sometimes desirable to control whenever a `Source` should be yielded or not by determining if the user has configured a transformer or not.
Given a configuration such as:
```js
"transform": {
"^.*\\.foo$": "my-transformer"
"^.*:virtual$": "my-other-transformer"
}
```
`my-transformer` can then implement the following pattern:
```typescript
/* create a virtual filename */
const next = `${source.filename}:virtual`;
if (this.hasChain(next)) {
yield * this.chain(source, next);
}
```
By letting the user configure the `.*:virtual` pattern the user can control whenever `my-transformer` will yield a source for the match or not.
This is useful when the transformer needs to deal with multiple languages and the user should ultimately be able to control whenever a language should be validated by HTML-validate or not.
## `TemplateExtractor`
......
This diff is collapsed.
{
"name": "html-validate",
"version": "2.2.0",
"version": "2.3.0",
"description": "html linter",
"keywords": [
"html",
......@@ -136,8 +136,8 @@
"minimist": "^1.2.0"
},
"devDependencies": {
"@babel/core": "7.7.2",
"@babel/preset-env": "7.7.1",
"@babel/core": "7.7.4",
"@babel/preset-env": "7.7.4",
"@commitlint/cli": "8.2.0",
"@commitlint/config-conventional": "8.2.0",
"@semantic-release/changelog": "3.0.6",
......@@ -153,9 +153,9 @@
"@types/jest": "24.0.23",
"@types/json-merge-patch": "0.0.4",
"@types/minimist": "1.2.0",
"@types/node": "11.15.2",
"@typescript-eslint/eslint-plugin": "2.8.0",
"@typescript-eslint/parser": "2.8.0",
"@types/node": "11.15.3",
"@typescript-eslint/eslint-plugin": "2.9.0",
"@typescript-eslint/parser": "2.9.0",
"autoprefixer": "9.7.2",
"babelify": "10.0.0",
"bootstrap-sass": "3.4.1",
......
......@@ -18,18 +18,21 @@ exports[`should generate configuration for angularjs 1`] = `
exports[`should generate configuration for combined 1`] = `
"{
\\"elements\\": [
\\"html5\\",
\\"html-validate-vue/elements\\"
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\"
\\"htmlvalidate:recommended\\",
\\"html-valudate-vue:recommended\\"
],
\\"transform\\": {
\\"^.*\\\\\\\\.js$\\": \\"html-validate-angular/js\\",
\\"^.*\\\\\\\\.html$\\": \\"html-validate-angular/html\\",
\\"^.*\\\\\\\\.vue$\\": \\"html-validate-vue\\",
\\"^.*\\\\\\\\.md$\\": \\"html-validate-markdown\\"
}
},
\\"plugins\\": [
\\"html-validate-vue\\"
]
}"
`;
......@@ -61,11 +64,14 @@ exports[`should generate configuration for markdown 1`] = `
exports[`should generate configuration for vuejs 1`] = `
"{
\\"elements\\": [
\\"html5\\",
\\"html-validate-vue/elements\\"
\\"html5\\"
],
\\"extends\\": [
\\"htmlvalidate:recommended\\"
\\"htmlvalidate:recommended\\",
\\"html-valudate-vue:recommended\\"
],
\\"plugins\\": [
\\"html-validate-vue\\"
],
\\"transform\\": {
\\"^.*\\\\\\\\.vue$\\": \\"html-validate-vue\\"
......
......@@ -21,7 +21,8 @@ const frameworkConfig: Record<string, ConfigData> = {
},
},
[Frameworks.vuejs]: {
elements: ["html-validate-vue/elements"],
plugins: ["html-validate-vue"],
extends: ["html-valudate-vue:recommended"],
transform: {
"^.*\\.vue$": "html-validate-vue",
},
......
......@@ -499,6 +499,30 @@ describe("config", () => {
`);
});
it("should support testing if chain is present", () => {
const config = Config.fromObject({
transform: {
"^.*\\.foo$": "mock-transform-optional-chain",
"^.*\\.bar$": "mock-transform",
},
});
config.init();
source.filename = "/path/to/test.bar.foo";
expect(config.transformSource(source)).toMatchInlineSnapshot(`
Array [
Object {
"column": 1,
"data": "transformed source (was: data from mock-transform-optional-chain (was: original data))",
"filename": "/path/to/test.bar.foo",
"line": 1,
"originalData": "original data",
},
]
`);
source.filename = "/path/to/test.baz.foo";
expect(config.transformSource(source)).toEqual([]);
});
it("should replace <rootDir>", () => {
const config = Config.fromObject({
transform: {
......
......@@ -341,6 +341,9 @@ export class Config {
public transformSource(source: Source, filename?: string): Source[] {
const transformer = this.findTransformer(filename || source.filename);
const context: TransformContext = {
hasChain: (filename: string): boolean => {
return !!this.findTransformer(filename);
},
chain: (source: Source, filename: string) => {
return this.transformSource(source, filename);
},
......
import { Source } from "../../context";
import { Transformer, TransformContext, TRANSFORMER_API } from "..";
/**
* Mock transformer chaining to a new transformer by chopping of the current
* extension. E.g. "my-file.bar.foo" -> "my-file.bar".
*/
function* mockTransformOptionalChain(
this: TransformContext,
source: Source
): Iterable<Source> {
const next = source.filename.replace(/\.[^.]*$/, "");
if (this.hasChain(next)) {
yield* this.chain(
{
data: `data from mock-transform-optional-chain (was: ${source.data})`,
filename: source.filename,
line: 1,
column: 1,
originalData: source.originalData || source.data,
},
next
);
}
}
/* mocks are always written against current version */
mockTransformOptionalChain.api = TRANSFORMER_API.VERSION;
module.exports = mockTransformOptionalChain as Transformer;
import { Source } from "../context";
export interface TransformContext {
/**
* Test if an additional chainable transformer is present.
*
* Returns true only if there is a transformer configured for the given
* filename.
*
* @param filename - Filename to use to match next transformer.
*/
hasChain(filename: string): boolean;
/**
* Chain transformations.
*
......
......@@ -59,6 +59,7 @@ export function transformSource(
): Source[] {
const defaultChain = (source: Source): Iterable<Source> => [source];
const context: TransformContext = {
hasChain: /* istanbul ignore next */ () => true,
chain: chain || defaultChain,
};
return Array.from(fn.call(context, source));
......