Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
}
// It's ok to tag templates with the expression `Polymer.html` without
// adding an import because `Polymer.html` is re-exported by both
// polymer.html and polymer-element.html and, crucially, template
// inlining happens before rewriting references.
const templateLiteral = jsc.taggedTemplateExpression(
jsc.memberExpression(
jsc.identifier('Polymer'), jsc.identifier('html')),
serializeNodeToTemplateLiteral(
parse5.treeAdapters.default.getTemplateContent(template)));
const nodePath = getNodePathInProgram(program, element.astNode);
if (nodePath === undefined) {
console.warn(
new Warning({
code: 'not-found',
message: `Can't find recast node for element ${element.tagName}`,
parsedDocument: this.document.parsedDocument,
severity: Severity.WARNING,
sourceRange: element.sourceRange!
}).toString());
continue;
}
const node = nodePath.node;
if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
// A Polymer 2.0 class-based element
node.body.body.splice(
0,
0,
jsc.methodDefinition(
const indent = getIndentationInside(lastImport.parentNode!);
fix = [insertContentAfter(parsedDocument, lastImport, `
${indent}`)];
} else {
// If no imports present, assume we are in a file with only
// s as it doesn't make sense to be in a entry-point
// document w/o imports.
const range = {
file: parsedDocument.sourceRangeForStartTag(styleNode)!.file,
start: {line: 0, column: 0},
end: {line: 0, column: 0}
};
const replacementText = `\n`;
fix = [{replacementText, range}];
}
warnings.push(new Warning({
code: 'iron-flex-layout-import',
message: `iron-flex-layout style modules are used but not imported.
Import iron-flex-layout/iron-flex-layout-classes.html`,
parsedDocument,
severity: Severity.WARNING,
sourceRange:
parsedDocument.sourceRangeForAttributeValue(styleNode, 'include')!,
fix
}));
}
// If there is a good import that is not used, remove it.
if (!styleNode && goodImport) {
warnings.push(new Warning({
code: 'iron-flex-layout-import',
message:
`This import defines style modules that are not being used. It can be removed.`,
}
const domModule = domModulesByTagName.get(tagName);
if (!domModule || seenSoFar.has(tagName)) {
continue;
}
seenSoFar.add(tagName);
// Ok! Finally! domModule is a from `document`, and
// `element` is its element definition, first defined at
// `pair.sourceRange` in `document`. Now we compare them, and if the
// element comes before the dom-module, that's an error!
if (comparePositionAndRange(
pair.sourceRange.start, domModule.sourceRange) === -1) {
// TODO(rictic): if we ever support multiple source ranges on
// warnings, this would be a good candidate.
warnings.push(new Warning({
parsedDocument: parsedHtml,
code: this.code,
message: `A Polymer element must be defined after its ` +
`\`\`. If it can't find its \`\` ` +
`when it is registered, it will assume it does not have one.`,
severity: Severity.ERROR,
sourceRange:
parsedHtml.sourceRangeForStartTag(domModule.astNode.node) ||
domModule.sourceRange,
}));
}
}
}
return warnings;
}
suspiciousPropertiesByName.set(prop.name, props);
}
}
// TODO(rictic): also validate computed properties and observers once
// https://github.com/Polymer/polymer-analyzer/pull/552 has
// landed.
for (const usesOfProperty of suspiciousPropertiesByName.values()) {
const firstUse = usesOfProperty[0];
if (!firstUse) {
throw new Error('This should never happen');
}
if (usesOfProperty.length === 1) {
const bestGuess =
closestSpelling(firstUse.name, explicitlyKnownProperties)!.min;
warnings.push(new Warning({
parsedDocument,
code: this.code,
severity: Severity.WARNING,
sourceRange: firstUse.sourceRange,
message:
`${firstUse.name} is not declared or used more than once. ` +
`Did you mean: ${bestGuess}`
}));
continue;
}
const hasWrite = !!usesOfProperty.find((use) => {
// We're writing into a property if it's on an attribute (not e.g. a
// text node), if it's a bidirectional binding, and it's the only
// property in the expression. (this isn't perfect, it misses some
// strange stuff with literals like foo(10, 20) but it's good enough.)
return use.expression instanceof AttributeDatabindingExpression &&
methodName = method.key.name;
}
}
if (!methodName) {
continue;
}
// Ok, so now just check that the method does call super.methodName()
if (!doesCallSuper(method, methodName)) {
// Construct a nice legible warning.
const parsedDocumentContaining =
getDocumentContaining(elementLike.sourceRange, document);
if (parsedDocumentContaining) {
const sourceRange =
parsedDocumentContaining.sourceRangeForNode(method.key)!;
if (method.kind === 'constructor') {
warnings.push(new Warning({
parsedDocument: document.parsedDocument,
code: 'call-super-in-constructor',
severity: Severity.ERROR, sourceRange,
message: stripWhitespace(`
ES6 requires super() in constructors with superclasses.
`)
}));
} else {
let message;
let code;
if (elementLike instanceof ElementMixin) {
code = 'call-super-in-mixin-callbacks';
message = stripWhitespace(`
This method should conditionally call super.${methodName}()
because a class ${getName(elementLike, 'this mixin')} is
applied to may also define ${methodName}.`);
}
// Early exit if there's nothing to check.
if (mustBeMethods.length === 0) {
continue;
}
const potentialMethodNames = new Set(
Array.from(element.properties.values())
.filter((p) => !p.type || !definitelyNotMethodTypes.has(p.type))
.map((p) => p.name)
.concat([...element.methods.keys()]));
const elementName =
element.tagName || element.className || 'this element';
for (const mustBeMethod of mustBeMethods) {
if (!potentialMethodNames.has(mustBeMethod.name)) {
warnings.push(new Warning({
parsedDocument: document.parsedDocument,
code: this.code,
message: stripWhitespace(`
${mustBeMethod.name} is not a known method on ${elementName}`),
severity: Severity.WARNING,
sourceRange: mustBeMethod.sourceRange
}));
}
}
}
return warnings;
}
}
methodName = method.key.name;
}
}
if (!methodName) {
continue;
}
// Ok, so now just check that the method does call super.methodName()
if (!doesCallSuper(method, methodName)) {
// Construct a nice legible warning.
const parsedDocumentContaining =
getDocumentContaining(elementLike.sourceRange, document);
if (parsedDocumentContaining) {
const sourceRange =
parsedDocumentContaining.sourceRangeForNode(method.key)!;
if (method.kind === 'constructor') {
warnings.push(new Warning({
parsedDocument: document.parsedDocument,
code: 'call-super-in-constructor',
severity: Severity.ERROR, sourceRange,
message: stripWhitespace(`
ES6 requires super() in constructors with superclasses.
`)
}));
} else {
let message;
let code;
if (elementLike instanceof ElementMixin) {
code = 'call-super-in-mixin-callbacks';
message = stripWhitespace(`
This method should conditionally call super.${methodName}()
because a class ${getName(elementLike, 'this mixin')} is
applied to may also define ${methodName}.`);
const claimedDomModules = new Set();
let prevScriptNode: parse5.ASTNode|undefined = undefined;
/**
* Inserting leading comments is surprisingly tricky, because they must
* be attached to a node, but very few nodes can come before an import,
* and in general we don't want to insert fake nodes, so instead we
* pass these up to be added onto the first node in the final output script.
*/
let htmlCommentsBeforeFirstScriptNode: undefined|string[];
for (const script of this.document.getFeatures()) {
let scriptDocument: Document;
if (script.kinds.has('html-script')) {
const scriptImport = script as Import;
if (!isImportWithDocument(scriptImport)) {
console.warn(
new Warning({
code: 'import-ignored',
message: `Import could not be loaded and will be ignored.`,
parsedDocument: this.document.parsedDocument,
severity: Severity.WARNING,
sourceRange: scriptImport.sourceRange!,
}).toString());
continue;
}
if (!this.isInternalNonModuleImport(scriptImport)) {
continue;
}
scriptDocument = scriptImport.document;
convertedHtmlScripts.add(scriptImport);
} else if (script.kinds.has('js-document')) {
private get _isWrapperHTMLDocument() {
const allFeatures = Array.from(this.document.getFeatures())
.filter(
(f) =>
!(f.kinds.has('html-document') &&
(f as Document).isInline === false));
if (allFeatures.length === 1) {
const f = allFeatures[0];
if (f.kinds.has('html-script')) {
const scriptImport = f as Import;
if (!isImportWithDocument(scriptImport)) {
console.warn(
new Warning({
code: 'import-ignored',
message: `Import could not be loaded and will be ignored.`,
parsedDocument: this.document.parsedDocument,
severity: Severity.WARNING,
sourceRange: scriptImport.sourceRange!,
}).toString());
return false;
}
const oldScriptUrl =
this.urlHandler.getDocumentUrl(scriptImport.document);
const newScriptUrl = this.convertScriptUrl(oldScriptUrl);
return newScriptUrl === this.convertedUrl;
}
}
return false;
}
private getHtmlImports(): Array {
const filteredImports = [];
for (const import_ of DocumentConverter.getAllHtmlImports(this.document)) {
if (!isImportWithDocument(import_)) {
console.warn(
new Warning({
code: 'import-ignored',
message: `Import could not be loaded and will be ignored.`,
parsedDocument: this.document.parsedDocument,
severity: Severity.WARNING,
sourceRange: import_.sourceRange!,
}).toString());
continue;
}
const documentUrl = this.urlHandler.getDocumentUrl(import_.document);
if (this.conversionSettings.excludes.has(documentUrl)) {
continue;
}
filteredImports.push(import_);
}