"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.processScript = exports.isScriptProcessed = void 0; const transform_1 = __importDefault(require("./transform")); const instruction_1 = __importDefault(require("./instruction")); const header_1 = require("./header"); const acorn_hammerhead_1 = require("acorn-hammerhead"); const esotope_hammerhead_1 = require("esotope-hammerhead"); const regexp_escape_1 = __importDefault(require("../../utils/regexp-escape")); const get_bom_1 = __importDefault(require("../../utils/get-bom")); const HTML_COMMENT_RE = /(^|\n)\s*<!--[^\n]*(\n|$)/g; const OBJECT_RE = /^\s*\{.*\}\s*$/; const TRAILING_SEMICOLON_RE = /;\s*$/; const OBJECT_WRAPPER_RE = /^\s*\((.*)\);\s*$/; const SOURCEMAP_RE = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)/gm; const PROCESSED_SCRIPT_RE = new RegExp([ (0, regexp_escape_1.default)(instruction_1.default.getLocation), (0, regexp_escape_1.default)(instruction_1.default.setLocation), (0, regexp_escape_1.default)(instruction_1.default.getProperty), (0, regexp_escape_1.default)(instruction_1.default.setProperty), (0, regexp_escape_1.default)(instruction_1.default.callMethod), (0, regexp_escape_1.default)(instruction_1.default.processScript), (0, regexp_escape_1.default)(instruction_1.default.processHtml), (0, regexp_escape_1.default)(instruction_1.default.getPostMessage), (0, regexp_escape_1.default)(instruction_1.default.getProxyUrl), ].join('|')); const PARSING_OPTIONS = { allowReturnOutsideFunction: true, allowImportExportEverywhere: true, ecmaVersion: 13, }; // Code pre/post-processing function removeHtmlComments(code) { // NOTE: The JS parser removes the line that follows'<!--'. (T226589) do code = code.replace(HTML_COMMENT_RE, '\n'); while (HTML_COMMENT_RE.test(code)); return code; } function preprocess(code) { const bom = (0, get_bom_1.default)(code); let preprocessed = bom ? code.substring(bom.length) : code; preprocessed = (0, header_1.remove)(preprocessed); preprocessed = removeSourceMap(preprocessed); return { bom, preprocessed }; } function removeSourceMap(code) { return code.replace(SOURCEMAP_RE, ''); } function postprocess(processed, withHeader, bom, strictMode, swScopeHeaderValue, proxyless, workerSettings) { // NOTE: If the 'use strict' directive is not in the beginning of the file, it is ignored. // As we insert our header in the beginning of the script, we must put a new 'use strict' // before the header, otherwise it will be ignored. if (withHeader) processed = (0, header_1.add)(processed, strictMode, swScopeHeaderValue, proxyless, workerSettings); return bom ? bom + processed : processed; } // Parse/generate code function removeTrailingSemicolon(processed, src) { return TRAILING_SEMICOLON_RE.test(src) ? processed : processed.replace(TRAILING_SEMICOLON_RE, ''); } function getAst(src, isObject) { // NOTE: In case of objects (e.g.eval('{ 1: 2}')) without wrapping // object will be parsed as label. To avoid this we parenthesize src src = isObject ? `(${src})` : src; try { return (0, acorn_hammerhead_1.parse)(src, PARSING_OPTIONS); } catch (err) { return null; } } function getCode(ast, src) { const code = (0, esotope_hammerhead_1.generate)(ast, { format: { quotes: 'double', escapeless: true, compact: true, }, }); return src ? removeTrailingSemicolon(code, src) : code; } // Analyze code function analyze(code) { let isObject = OBJECT_RE.test(code); let ast = getAst(code, isObject); // NOTE: `{ const a = 'foo'; }` edge case if (!ast && isObject) { ast = getAst(code, false); isObject = false; } return { ast, isObject }; } function isArrayDataScript(ast) { const firstChild = ast.body[0]; return ast.body.length === 1 && firstChild.type === esotope_hammerhead_1.Syntax.ExpressionStatement && firstChild.expression.type === esotope_hammerhead_1.Syntax.ArrayExpression; } function isStrictMode(ast) { if (ast.body.length) { const firstChild = ast.body[0]; if (firstChild.type === esotope_hammerhead_1.Syntax.ExpressionStatement && firstChild.expression.type === esotope_hammerhead_1.Syntax.Literal) return firstChild.expression.value === 'use strict'; } return false; } function applyChanges(script, changes, isObject) { const indexOffset = isObject ? -1 : 0; const chunks = []; let index = 0; if (!changes.length) return script; changes.sort((a, b) => (a.start - b.start) || (a.end - b.end) || // eslint-disable-line @typescript-eslint/no-extra-parens ((a.node.type === esotope_hammerhead_1.Syntax.VariableDeclaration ? 0 : 1) - (b.node.type === esotope_hammerhead_1.Syntax.VariableDeclaration ? 0 : 1))); // eslint-disable-line @typescript-eslint/no-extra-parens for (const change of changes) { const changeStart = change.start + indexOffset; const changeEnd = change.end + indexOffset; const parentheses = change.node.type === esotope_hammerhead_1.Syntax.SequenceExpression && change.parentType !== esotope_hammerhead_1.Syntax.ExpressionStatement && change.parentType !== esotope_hammerhead_1.Syntax.SequenceExpression; chunks.push(script.substring(index, changeStart)); chunks.push(parentheses ? '(' : ' '); chunks.push(getCode(change.node, script.substring(changeStart, changeEnd))); chunks.push(parentheses ? ')' : ' '); index += changeEnd - index; } chunks.push(script.substring(index)); return chunks.join(''); } function isScriptProcessed(code) { return PROCESSED_SCRIPT_RE.test(code); } exports.isScriptProcessed = isScriptProcessed; function processScript(src, withHeader = false, wrapLastExprWithProcessHtml = false, resolver, swScopeHeaderValue, proxyless, workerSettings) { const { bom, preprocessed } = preprocess(src); const withoutHtmlComments = removeHtmlComments(preprocessed); const { ast, isObject } = analyze(withoutHtmlComments); if (!ast) return src; withHeader = withHeader && !isObject && !isArrayDataScript(ast); const changes = proxyless ? [] : (0, transform_1.default)(ast, wrapLastExprWithProcessHtml, resolver); let processed = changes.length ? applyChanges(withoutHtmlComments, changes, isObject) : preprocessed; processed = postprocess(processed, withHeader, bom, isStrictMode(ast), swScopeHeaderValue, proxyless, workerSettings); if (isObject) processed = processed.replace(OBJECT_WRAPPER_RE, '$1'); return processed; } exports.processScript = processScript;