Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
resolve();
return;
}
const virtualConsole = new VirtualConsole();
virtualConsole.on('error', (err: Error) => {
debug(`Console: ${err}`);
});
virtualConsole.on('jsdomError', (err) => {
debug(`Console: ${err}`);
});
const initialDocument = createHTMLDocument(fetchEnd.response.body.content, this.finalHref);
this._resourceLoader = new CustomResourceLoader(this, initialDocument);
const jsdom = new JSDOM(this._targetNetworkData.response.body.content, {
beforeParse: beforeParse(this.finalHref),
// includeNodeLocations: true, // TODO: re-enable once locations can be copied from snapshot.
pretendToBeVisual: true,
resources: this._resourceLoader,
runScripts: 'dangerously',
url: this.finalHref,
virtualConsole
});
const window = jsdom.window;
this._window = window;
setTimeout(async () => {
const event: Event = { resource: this.finalHref };
debug(`${this.finalHref} loaded, traversing`);
try {
// TODO: Use a DOM snapshot and copy node locations insead of serializing.
const html = this._window.document.documentElement.outerHTML;
const htmlDocument = createHTMLDocument(html, this.finalHref, this._originalDocument);
this._document = htmlDocument;
const evaluateEvent = {
document: htmlDocument,
resource: this.finalHref
};
await this.server.emitAsync('can-evaluate::script', evaluateEvent);
await traverse(htmlDocument, this.server, this.finalHref);
// We download only the first favicon found
await this.getFavicon(window.document.querySelector('link[rel~="icon"]'));
/*
private async processTarget() {
await this.waitForTarget();
// QUESTION: Even if the content is blank we will receive a minimum HTML with this. Are we OK with the behavior?
const html = await this._page.content();
this._dom = createHTMLDocument(html, this._finalHref, this._originalDocument);
// Process pending requests now that the dom is ready
while (this._pendingRequests.length > 0) {
const pendingRequest = this._pendingRequests.shift()!;
await pendingRequest();
}
if (this._options.headless) {
// TODO: Check if browser downloads favicon even if there's no content
await getFavicon(this._dom, this.fetchContent.bind(this), this._engine);
}
if (this._targetBody) {
await traverse(this._dom, this._engine, this._page.url());
const event = (await onResponseHandler(response, this.fetchContent.bind(this), this._dom));
if (!event) {
this._pendingRequests.push(this.onResponse.bind(this, response));
return;
}
const { name, payload } = event;
if (isTarget) {
this._targetBody = payload.response.body.content;
this._targetNetworkData = payload;
if (name === 'fetch::end::html') {
this._originalDocument = createHTMLDocument(this._targetBody!, resource);
}
}
await this._engine.emitAsync(name, payload);
// The `fetch::end` of the target needs to be processed before notifying
/* istanbul ignore if */
if (isTarget && this._targetReady) {
this._targetReady();
}
}
/**
* We search the first URL because it will be the one
* that was originally specified in the DOM.
*/
const redirectChain = request.redirectChain();
const requestUrl = redirectChain && redirectChain.length > 0 ?
redirectChain[0].url() :
source.url();
/*
* TODO: Check what happens with prefetch, etc.
* `type` can be "parser", "script", "preload", and "other": https://chromedevtools.github.io/debugger-protocol-viewer/tot/Network/#type-Initiator
*/
// The doesn't seem to be an initiator in puppeteer :/
if (dom && requestUrl.startsWith('http')) {
return getElementByUrl(dom, requestUrl);
}
return null;
};
public async fetch(url: string): Promise {
/* istanbul ignore if */
if (!url) {
const promise = Promise.resolve(null);
(promise as any).abort = () => { };
return await promise;
}
/*
* We need a `HTMLElement` instead of the element returned by jsdom.
* To do so, we create a query from the element returned by jsdom and
* look for it in our `HTMLDocument`.
*/
const element = getElementByUrl(this._HTMLDocument, url);
const urlAsUrl = new URL(url);
let resourceUrl: string = urlAsUrl.href;
/* istanbul ignore if */
if (!urlAsUrl.protocol) {
resourceUrl = new URL(resourceUrl, this._connector.finalHref).href;
}
// Ignore if the resource has already been fetched.
if (this._connector.fetchedHrefs.has(resourceUrl)) {
return null;
}
this._connector.fetchedHrefs.add(resourceUrl);
debug(`resource ${resourceUrl} to be fetched`);
setTimeout(async () => {
try {
await this.evaluateInPage(`(${createHelpers})()`);
const snapshot: DocumentData = await this.evaluateInPage('__webhint.snapshotDocument(document)');
restoreReferences(snapshot);
this._document = new HTMLDocument(snapshot, location.href, this._originalDocument);
await this.sendFetchEndEvents();
await traverse(this._document, this._engine, resource);
/*
* Evaluate after the traversing, just in case something goes wrong
* in any of the evaluation and some scripts are left in the DOM.
*/
const event = {
document: this._document,
resource
};
await this._engine.emitAsync('can-evaluate::script', event);
this._onComplete(null, resource);
} catch (err) /* istanbul ignore next */ {
this._onComplete(err);
this._dom = createHTMLDocument(html, this._finalHref, this._originalDocument);
// Process pending requests now that the dom is ready
while (this._pendingRequests.length > 0) {
const pendingRequest = this._pendingRequests.shift()!;
await pendingRequest();
}
if (this._options.headless) {
// TODO: Check if browser downloads favicon even if there's no content
await getFavicon(this._dom, this.fetchContent.bind(this), this._engine);
}
if (this._targetBody) {
await traverse(this._dom, this._engine, this._page.url());
const event = {
document: this._dom,
resource: this._finalHref
};
await this._engine.emitAsync('can-evaluate::script', event);
}
}
try {
// TODO: Use a DOM snapshot and copy node locations insead of serializing.
const html = this._window.document.documentElement.outerHTML;
const htmlDocument = createHTMLDocument(html, this.finalHref, this._originalDocument);
this._document = htmlDocument;
const evaluateEvent = {
document: htmlDocument,
resource: this.finalHref
};
await this.server.emitAsync('can-evaluate::script', evaluateEvent);
await traverse(htmlDocument, this.server, this.finalHref);
// We download only the first favicon found
await this.getFavicon(window.document.querySelector('link[rel~="icon"]'));
/*
* TODO: when we reach this moment we should wait for all pending request to be done and
* stop processing any more.
*/
await this.server.emitAsync('scan::end', event);
} catch (e) /* istanbul ignore next */ {
reject(e);
}
resolve();
}, this._options.waitFor);
};
public report(resource: string, message: string, options: ReportOptions) {
const { codeSnippet, element, severity = Severity.warning } = options;
let sourceCode: string | null = null;
let position = options.location || null;
if (element) {
// When element is provided, position is an offset in the content.
position = this.findProblemLocation(element, position);
sourceCode = getHTMLCodeSnippet(element);
}
/**
* By default all hints get configured with `default` so they can
* decide the severity of each report unless it's overriden by the
* user.
*/
const finalSeverity = this.severity !== Severity.default ?
this.severity :
severity;
/*
* If location is undefined or equal to null, `position` will be set as `{ column: -1, line: -1 }` later in `hint.report`.
* So pass the `location` on as it is.
*/