1// Copyright 2019 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import fs from 'fs'; 6import path from 'path'; 7import {parse, print} from 'recast'; 8import {promisify} from 'util'; 9 10const readFile = promisify(fs.readFile); 11const stat = promisify(fs.stat); 12 13const FRONT_END_FOLDER = path.join(__dirname, '..', '..', 'front_end'); 14 15// eslint-disable-next-line @typescript-eslint/no-explicit-any 16export async function getMappings(namespace: string, mappings: Map<string, any>, useExternalRefs = false) { 17 const src = namespace.toLocaleLowerCase(); 18 const externalModule = path.join(FRONT_END_FOLDER, src, `${src}.js`); 19 const legacy = path.join(FRONT_END_FOLDER, src, `${src}-legacy.js`); 20 21 if (!(await stat(legacy))) { 22 console.error(`Unable to find legacy file: ${legacy}`); 23 process.exit(1); 24 } 25 26 const legacyFileContents = await readFile(legacy, { encoding: 'utf-8' }); 27 const ast = parse(legacyFileContents); 28 29 for (const statement of ast.program.body) { 30 if (statement.type !== 'ExpressionStatement') { 31 continue; 32 } 33 34 // We need to check that we have an assignment expression, of which the left and right are both member expressions. 35 // This allows us to extract things like Foo.Bar = FooModule.Bar.Bar from the legacy file, while ignoring self.Foo 36 // and Foo = Foo || {} statements. 37 const isMemberExpressionOnLeftAndRight = statement.expression && statement.expression.left && 38 statement.expression.right && statement.expression.type === 'AssignmentExpression' && 39 statement.expression.left.type === 'MemberExpression' && statement.expression.right.type === 'MemberExpression'; 40 41 if (isMemberExpressionOnLeftAndRight) { 42 // Rename FooModule back to Foo because we know we will want to use the latter when doing replacements. 43 if (statement.expression.right.object && statement.expression.right.object.object && 44 statement.expression.right.object.object.type === 'Identifier') { 45 statement.expression.right.object.object.name = statement.expression.right.object.object.name.replace(/Module$/, ''); 46 } 47 48 const leftSide = print(statement.expression.left).code; 49 const rightSide = print(statement.expression.right).code; 50 const rightSideParts = rightSide.split('.'); 51 const file = useExternalRefs ? externalModule : path.join(FRONT_END_FOLDER, src, rightSideParts[1] + '.js'); 52 53 if (rightSideParts[0] === 'Protocol') { 54 rightSideParts[0] = 'ProtocolClient'; 55 } 56 57 mappings.set(leftSide, { 58 file, 59 replacement: rightSideParts.join('.'), 60 sameFolderReplacement: rightSideParts[rightSideParts.length - 1], 61 }); 62 } 63 } 64 65 return mappings; 66} 67