Skip to content
Permalink
Browse files

New: Include element reference if source was inline in HTML

Allow hints for CSS and JavaScript source to correctly offset the
location of reported issues nested inside an HTML document.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Close #1953
  • Loading branch information...
antross authored and molant committed Feb 22, 2019
1 parent a5d2ab0 commit 173e35bccd1296f499031df8001e021b34439469
@@ -42,6 +42,7 @@ This `parser` emits the following events:
See the [PostCSS `walk*` APIs][postcss-walk] for help navigating
the AST.
* `code`: a string containing the raw stylesheet source code.
* `element`: an `IAsyncHTMLElement` reference if the source was inline in HTML; `null` otherwise.
* `resource`: the parsed resource. If the CSS is in a `style tag`
and not a file, the value will be `Inline CSS`.

@@ -20,7 +20,7 @@ export default class CSSParser extends Parser<StyleEvents> {
engine.on('element::style', this.parseStyleTag.bind(this));
}

private async emitCSS(code: string, resource: string) {
private async emitCSS(code: string, resource: string, element: IAsyncHTMLElement | null) {

try {
await this.engine.emitAsync(`parse::start::css`, { resource });
@@ -31,6 +31,7 @@ export default class CSSParser extends Parser<StyleEvents> {
await this.engine.emitAsync(`parse::end::css`, {
ast,
code,
element,
resource
});

@@ -43,7 +44,7 @@ export default class CSSParser extends Parser<StyleEvents> {
const code = fetchEnd.response.body.content;
const resource = fetchEnd.resource;

await this.emitCSS(code, resource);
await this.emitCSS(code, resource, null);
}

private isCSSType(element: IAsyncHTMLElement) {
@@ -76,6 +77,6 @@ export default class CSSParser extends Parser<StyleEvents> {
const code = this.getStyleContent(await element.outerHTML());
const resource: string = 'Inline CSS';

await this.emitCSS(code, resource);
await this.emitCSS(code, resource, element);
}
}
@@ -1,3 +1,4 @@
import { IAsyncHTMLElement } from 'hint/dist/src/lib/types';
import { Event, Events } from 'hint/dist/src/lib/types/events';
import { Root } from 'postcss';

@@ -11,6 +12,8 @@ export type StyleParse = Event & {
ast: Root;
/** The raw stylesheet source code */
code: string;
/** The originating <style> element if the CSS was inline */
element: IAsyncHTMLElement | null;
};

export type StyleEvents = Events & {
@@ -8,5 +8,5 @@ export const mockStyleElement = (type: string, code: string) => {
outerHTML() {
return Promise.resolve(`<style> ${code} </style>`);
}
} as Partial<IAsyncHTMLElement>;
} as Partial<IAsyncHTMLElement> as IAsyncHTMLElement;
};
@@ -77,6 +77,7 @@ test('If a style tag is inline CSS, then we should parse the stylesheet and emit

t.is(args[0], 'parse::end::css');
t.is(data.code, code);
t.is(data.element, element);
t.is(data.resource, 'Inline CSS');
});

@@ -105,5 +106,6 @@ test('If fetch::end::css is received, then we should parse the stylesheet and em

t.is(args[0], 'parse::end::css');
t.is(data.code, code);
t.is(data.element, null);
t.is(data.resource, 'styles.css');
});
@@ -40,10 +40,12 @@ This `parser` emits the following events:
* `parse::end::javascript`, of type `ScriptParse` which contains the following
information:

* `resource`: the parsed resource. If the JavaScript is in
a `script tag` and not a file, the value will be `Internal
javascript`.
* `sourceCode`: a `eslint` `SourceCode` object.
* `ast`: an ESLint `AST.Program` object containing the parsed AST.
* `element`: an `IAsyncHTMLElement` reference if the source was inline in HTML; `null` otherwise.
* `resource`: the parsed resource. If the JavaScript is in
a `script tag` and not a file, the value will be `Internal
javascript`.
* `sourceCode`: a `eslint` `SourceCode` object.

<!-- Link labels: -->

@@ -28,14 +28,15 @@ export default class JavascriptParser extends Parser<ScriptEvents> {
engine.on('element::script', this.parseJavascriptTag.bind(this));
}

private async emitScript(code: string, resource: string) {
private async emitScript(code: string, resource: string, element: IAsyncHTMLElement | null) {
try {
await this.engine.emitAsync(`parse::start::javascript`, { resource });

const ast: AST.Program = espree.parse(code, defaultParserOptions);

await this.engine.emitAsync(`parse::end::javascript`, {
ast,
element,
resource,
sourceCode: new SourceCode(code, ast)
});
@@ -48,7 +49,7 @@ export default class JavascriptParser extends Parser<ScriptEvents> {
const code = fetchEnd.response.body.content;
const resource = fetchEnd.resource;

await this.emitScript(code, resource);
await this.emitScript(code, resource, null);
}

private hasSrcAttribute(element: IAsyncHTMLElement) {
@@ -91,6 +92,6 @@ export default class JavascriptParser extends Parser<ScriptEvents> {
const code = this.getScriptContent(await element.outerHTML());
const resource: string = 'Internal javascript';

await this.emitScript(code, resource);
await this.emitScript(code, resource, element);
}
}
@@ -1,11 +1,14 @@
import { AST, SourceCode } from 'eslint';

import { IAsyncHTMLElement } from 'hint/dist/src/lib/types';
import { Event, Events } from 'hint/dist/src/lib/types/events';

/** The object emitted by the `javascript` parser */
export type ScriptParse = Event & {
/** The ast generated from the script */
ast: AST.Program;
/** The originating <script> element if the script was inline */
element: IAsyncHTMLElement | null;
/** The source code parsed */
sourceCode: SourceCode;
};
@@ -151,6 +151,7 @@ test('If an script tag is an internal javascript, then we should parse the code
const data = args[1] as ScriptParse;

t.is(args[0], 'parse::end::javascript');
t.is(data.element, t.context.element);
t.is(data.resource, 'Internal javascript');
t.is(data.sourceCode, sourceCodeObject);
});
@@ -189,6 +190,7 @@ test('If fetch::end::script is received, then we should parse the code and emit
const data = args[1] as ScriptParse;

t.is(args[0], 'parse::end::javascript');
t.is(data.element, null);
t.is(data.sourceCode, sourceCodeObject);
t.is(data.resource, 'script.js');
});

0 comments on commit 173e35b

Please sign in to comment.
You can’t perform that action at this time.