function transformEsModuleToAmd(script, jsOptions) { // We're not a module anymore. dom5.removeAttribute(script, 'type'); if (html_splitter_1.scriptWasSplitByHtmlSplitter(script)) { // Nothing else to do here. If we're using HtmlSplitter, the JsTransformer // is responsible for doing this transformation. return; } const isExternal = dom5.hasAttribute(script, 'src'); if (isExternal) { const src = dom5.getAttribute(script, 'src'); dom5.removeAttribute(script, 'src'); dom5.setTextContent(script, `define(['${src}']);`); } else { // Transform inline scripts with the AMD Babel plugin transformer. const newJs = js_transform_1.jsTransform(dom5.getTextContent(script), Object.assign({}, jsOptions, { transformModulesToAmd: true })); dom5.setTextContent(script, newJs); } }
!allScripts.some((node) => dom5.hasAttribute(node, 'nomodule'));
/** * Transform some HTML according to the given options. */ function htmlTransform(html, options) { if (options.js && options.js.moduleResolution === 'node' && !options.js.filePath) { throw new Error('Cannot perform node module resolution without filePath.'); } const document = parse5.parse(html, { locationInfo: true, }); removeFakeNodes(document); const allScripts = [...dom5.queryAll(document, isJsScript)]; const shouldTransformEsModuleToAmd = options.js && options.js.transformModulesToAmd && // Assume that if this document has a nomodule script, the author is // already handling browsers that don't support modules, and we don't // need to transform them (even if the configuration was set). // TODO(aomarks) Check this for HtmlSplitter case too. !allScripts.some((node) => dom5.hasAttribute(node, 'nomodule')); let wctScript, firstModuleScript; for (const script of allScripts) { const isModule = dom5.getAttribute(script, 'type') === 'module'; if (isModule) { if (firstModuleScript === undefined) { firstModuleScript = script; } if (shouldTransformEsModuleToAmd) { transformEsModuleToAmd(script, options.js); continue; // Bypass the standard jsTransform below. } } const isInline = !dom5.hasAttribute(script, 'src'); if (isInline) { // Note that scripts split by HtmlSplitter are always external, so we // don't have to check for that case here. const newJs = js_transform_1.jsTransform(dom5.getTextContent(script), Object.assign({}, options.js, { transformModulesToAmd: false })); dom5.setTextContent(script, newJs); } else if (wctScript === undefined) { const src = dom5.getAttribute(script, 'src') || ''; if (src.includes('web-component-tester/browser.js') || src.includes('wct-browser-legacy/browser.js') || src.includes('wct-mocha/wct-mocha.js')) { wctScript = script; } } } if (options.injectAmdLoader && shouldTransformEsModuleToAmd && firstModuleScript !== undefined) { const fragment = parse5.parseFragment('<script></script>\n'); dom5.setTextContent(fragment.childNodes[0], externalJs.getAmdLoader()); const amdLoaderScript = fragment.childNodes[0]; // Inject as late as possible (just before the first module is declared, if // there is one) because there may be some UMD dependencies that we want to // continue to load in global mode instead of AMD mode (which is detected by // the presence of the `require` global). // TODO(aomarks) If we don't define require, we can inject earlier. dom5.insertBefore(firstModuleScript.parentNode, firstModuleScript, fragment); if (wctScript !== undefined) { addWctTimingHack(wctScript, amdLoaderScript); } } const injectScript = (js) => { const fragment = parse5.parseFragment('<script></script>\n'); dom5.setTextContent(fragment.childNodes[0], js); const firstJsScriptOrHtmlImport = dom5.query(document, isJsScriptOrHtmlImport); if (firstJsScriptOrHtmlImport) { dom5.insertBefore(firstJsScriptOrHtmlImport.parentNode, firstJsScriptOrHtmlImport, fragment); } else { const headOrDocument = dom5.query(document, dom5.predicates.hasTagName('head')) || document; dom5.append(headOrDocument, fragment); } }; let babelHelpers; switch (options.injectBabelHelpers) { case undefined: case 'none': break; case 'full': babelHelpers = externalJs.getBabelHelpersFull(); break; case 'amd': babelHelpers = externalJs.getBabelHelpersAmd(); break; default: const never = options.injectBabelHelpers; throw new Error(`Unknown injectBabelHelpers value: ${never}`); } if (babelHelpers !== undefined) { injectScript(babelHelpers); } if (options.injectRegeneratorRuntime === true) { injectScript(externalJs.getRegeneratorRuntime()); } html = parse5.serialize(document); if (options.minifyHtml) { html = htmlMinifier.minify(html, { collapseWhitespace: true, removeComments: true, }); } return html; }
return __awaiter(this, void 0, void 0, function* () { const warnings = []; const elements = document.getFeatures({ kind: 'element' }); for (const element of elements) { // Look for selector errors in locally defined elements. const result = determineMigrationDescriptors(element); if (!result.success) { for (const error of result.value) { if (error.slot.astNode === undefined || error.slot.astNode.language !== 'html') { continue; } warnings.push(new polymer_analyzer_1.Warning({ code: 'invalid-old-content-selector', parsedDocument, severity: polymer_analyzer_1.Severity.WARNING, message: error.message, sourceRange: parsedDocument.sourceRangeForAttributeValue(error.slot.astNode.node, 'old-content-selector') || parsedDocument.sourceRangeForStartTag(error.slot.astNode.node) })); } } } const references = document.getFeatures({ kind: 'element-reference' }); for (const reference of references) { const contentDescriptors = getMigrationDescriptors(reference.tagName, document); if (!contentDescriptors) { continue; } const fix = []; const matchedSoFar = new Set(); for (const { predicate, slot } of contentDescriptors) { const matchingLightContents = []; function matchChildNodes(node) { for (const child of node.childNodes || []) { if (child.tagName === 'template') { const content = parse5_1.treeAdapters.default.getTemplateContent(child); matchChildNodes(content); } else if (predicate(child)) { matchingLightContents.push(child); } } } matchChildNodes(reference.astNode.node); for (const lightContent of matchingLightContents) { if (dom5.hasAttribute(lightContent, 'slot')) { continue; } const range = parsedDocument.sourceRangeForStartTag(lightContent); if (!range) { continue; } if (matchedSoFar.has(lightContent)) { continue; } matchedSoFar.add(lightContent); const [startOffset, endOffset] = parsedDocument.sourceRangeToOffsets(range); const originalText = parsedDocument.contents.slice(startOffset, endOffset); if (!originalText.endsWith('>')) { // Something weird is going on, don't make any changes. continue; } let justBeforeTagClose = -1; let tagCloseSyntax = '>'; if (originalText.endsWith('/>')) { justBeforeTagClose = -2; tagCloseSyntax = '/>'; } const withSlotAttr = originalText.slice(0, justBeforeTagClose) + ` slot="${slot}"${tagCloseSyntax}`; fix.push({ range, replacementText: withSlotAttr }); } } if (fix.length > 0) { warnings.push(new polymer_analyzer_1.Warning({ code: 'content-to-slot-usage-site', message: `Deprecated <content>-based distribution into ` + `<${reference.tagName}>. ` + `Must use the \`slot\` attribute for named distribution."`, parsedDocument, severity: polymer_analyzer_1.Severity.WARNING, sourceRange: parsedDocument.sourceRangeForStartTag(reference.astNode.node), fix })); } } return warnings; });