diff --git a/packages/messaging/sw/package.json b/packages/messaging/sw/package.json index 2eceb18b972..aac5e5ab2db 100644 --- a/packages/messaging/sw/package.json +++ b/packages/messaging/sw/package.json @@ -1,5 +1,5 @@ { - "name": "@firebase/messaging", + "name": "@firebase/messaging-sw", "description": "", "author": "Firebase (https://firebase.google.com/)", "module": "../dist/index.sw.esm2017.js", diff --git a/repo-scripts/size-analysis/analysis-helper.ts b/repo-scripts/size-analysis/analysis-helper.ts index 7e766c2a33f..db080306681 100644 --- a/repo-scripts/size-analysis/analysis-helper.ts +++ b/repo-scripts/size-analysis/analysis-helper.ts @@ -23,7 +23,7 @@ import * as terser from 'terser'; import * as ts from 'typescript'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; -import { deepCopy } from '@firebase/util'; +import { projectRoot } from '../../scripts/utils'; export const enum ErrorCode { INVALID_FLAG_COMBINATION = 'Invalid command flag combinations!', @@ -45,6 +45,7 @@ export interface MemberList { functions: string[]; variables: string[]; enums: string[]; + unknown: string[]; } /** Contains the dependencies and the size of their code for a single export. */ export interface ExportData { @@ -53,6 +54,7 @@ export interface ExportData { functions: string[]; variables: string[]; enums: string[]; + unknown: string[]; externals: { [key: string]: string[] }; size: number; sizeWithExtDeps: number; @@ -68,8 +70,7 @@ export interface Report { */ export async function extractDependenciesAndSize( exportName: string, - jsBundle: string, - map: Map + jsBundle: string ): Promise { const input = tmp.fileSync().name + '.js'; const externalDepsResolvedOutput = tmp.fileSync().name + '.js'; @@ -103,9 +104,8 @@ export async function extractDependenciesAndSize( file: externalDepsNotResolvedOutput, format: 'es' }); - const dependencies: MemberList = extractDeclarations( - externalDepsNotResolvedOutput, - map + const dependencies: MemberList = extractAllTopLevelSymbols( + externalDepsNotResolvedOutput ); const externalDepsResolvedOutputContent = fs.readFileSync( @@ -143,6 +143,7 @@ export async function extractDependenciesAndSize( functions: [], variables: [], enums: [], + unknown: [], externals: {}, size: 0, sizeWithExtDeps: 0 @@ -170,53 +171,23 @@ export async function extractDependenciesAndSize( } /** - * Extracts all function, class and variable declarations using the TypeScript - * compiler API. - * @param map maps every symbol listed in dts file to its type. eg: aVariable -> variable. - * map is null when given filePath is a path to d.ts file. - * map is populated when given filePath points to a .js bundle file. - * - * Examples of Various Type of Exports - * FunctionDeclaration: export function aFunc(): string {...}; - * ClassDeclaration: export class aClass {}; - * EnumDeclaration: export enum aEnum {}; - * VariableDeclaration: export let aVariable: string; import * as tmp from 'tmp'; export declare const aVar: tmp.someType. - * VariableStatement: export const aVarStatement: string = "string"; export const { a, b } = { a: 'a', b: 'b' }; - * ExportDeclaration: - * named exports: export {foo, bar} from '...'; export {foo as foo1, bar} from '...'; export {LogLevel}; - * export everything: export * from '...'; + * Check what symbols are being pulled into a bundle */ -export function extractDeclarations( - filePath: string, - map?: Map -): MemberList { +export function extractAllTopLevelSymbols(filePath: string): MemberList { const program = ts.createProgram([filePath], { allowJs: true }); - const checker = program.getTypeChecker(); - const sourceFile = program.getSourceFile(filePath); if (!sourceFile) { throw new Error(`${ErrorCode.FILE_PARSING_ERROR} ${filePath}`); } - let declarations: MemberList = { + const declarations: MemberList = { functions: [], classes: [], variables: [], - enums: [] + enums: [], + unknown: [] }; - const namespaceImportSet: Set = new Set(); - // define a map here which is used to handle export statements that have no from clause. - // As there is no from clause in such export statements, we retrieve symbol location by parsing the corresponding import - // statements. We store the symbol and its defined location as key value pairs in the map. - const importSymbolCurrentNameToModuleLocation: Map< - string, - string - > = new Map(); - const importSymbolCurrentNameToOriginalName: Map = new Map(); // key: current name value: original name - const importModuleLocationToExportedSymbolsList: Map< - string, - MemberList - > = new Map(); // key: module location, value: a list of all exported symbols of the module + ts.forEachChild(sourceFile, node => { if (ts.isFunctionDeclaration(node)) { declarations.functions.push(node.name!.text); @@ -253,304 +224,82 @@ export function extractDeclarations( }); } }); - } else if (ts.isImportDeclaration(node) && node.importClause) { - const symbol = checker.getSymbolAtLocation(node.moduleSpecifier); - if (symbol && symbol.valueDeclaration) { - const importFilePath = symbol.valueDeclaration.getSourceFile().fileName; - // import { a, b } from '@firebase/dummy-exp' - // import {a as A, b as B} from '@firebase/dummy-exp' - if ( - node.importClause.namedBindings && - ts.isNamedImports(node.importClause.namedBindings) - ) { - node.importClause.namedBindings.elements.forEach(each => { - const symbolName: string = each.name.getText(sourceFile); // import symbol current name - importSymbolCurrentNameToModuleLocation.set( - symbolName, - importFilePath - ); - // if imported symbols are renamed, insert an entry to importSymbolCurrentNameToOriginalName Map - // with key the current name, value the original name - if (each.propertyName) { - importSymbolCurrentNameToOriginalName.set( - symbolName, - each.propertyName.getText(sourceFile) - ); - } - }); - - // import * as fs from 'fs' - } else if ( - node.importClause.namedBindings && - ts.isNamespaceImport(node.importClause.namedBindings) - ) { - const symbolName: string = node.importClause.namedBindings.name.getText( - sourceFile - ); - namespaceImportSet.add(symbolName); - - // import a from '@firebase/dummy-exp' - } else if ( - node.importClause.name && - ts.isIdentifier(node.importClause.name) - ) { - const symbolName: string = node.importClause.name.getText(sourceFile); - importSymbolCurrentNameToModuleLocation.set( - symbolName, - importFilePath - ); - } - } - } - // re-exports handler: handles cases like : - // export {LogLevel}; - // export * from '..'; - // export {foo, bar} from '..'; - // export {foo as foo1, bar} from '...'; - else if (ts.isExportDeclaration(node)) { - // this clause handles the export statements that have a from clause (referred to as moduleSpecifier in ts compiler). - // examples are "export {foo as foo1, bar} from '...';" - // and "export * from '..';" - if (node.moduleSpecifier) { - if (ts.isStringLiteral(node.moduleSpecifier)) { - const reExportsWithFromClause: MemberList = handleExportStatementsWithFromClause( - checker, - node, - node.moduleSpecifier.getText(sourceFile) - ); - // concatenate re-exported MemberList with MemberList of the dts file - for (const key of Object.keys(declarations) as Array< - keyof MemberList - >) { - declarations[key].push(...reExportsWithFromClause[key]); - } - } - } else { - // export {LogLevel}; - // exclusively handles named export statements that has no from clause. - handleExportStatementsWithoutFromClause( - node, - importSymbolCurrentNameToModuleLocation, - importSymbolCurrentNameToOriginalName, - importModuleLocationToExportedSymbolsList, - namespaceImportSet, - declarations - ); - } } }); - declarations = dedup(declarations); - - if (map) { - declarations = mapSymbolToType(map, declarations); - } //Sort to ensure stable output - Object.values(declarations).map(each => { + Object.values(declarations).forEach(each => { each.sort(); }); return declarations; } + /** - * - * @param node compiler representation of an export statement - * - * This function exclusively handles export statements that have a from clause. The function uses checker argument to resolve - * module name specified in from clause to its actual location. It then retrieves all exported symbols from the module. - * If the statement is a named export, the function does an extra step, that is, filtering out the symbols that are not listed - * in exportClause. + * Extract exports of a module */ -function handleExportStatementsWithFromClause( - checker: ts.TypeChecker, - node: ts.ExportDeclaration, - moduleName: string -): MemberList { - const symbol = checker.getSymbolAtLocation(node.moduleSpecifier!); - let declarations: MemberList = { +export function extractExports(filePath: string): MemberList { + const exportDeclarations: MemberList = { functions: [], classes: [], variables: [], - enums: [] + enums: [], + unknown: [] }; - if (symbol && symbol.valueDeclaration) { - const reExportFullPath = symbol.valueDeclaration.getSourceFile().fileName; - // first step: always retrieve all exported symbols from the source location of the re-export. - declarations = extractDeclarations(reExportFullPath); - // if it's a named export statement, filter the MemberList to keep only those listed in exportClause. - // named exports: eg: export {foo, bar} from '...'; and export {foo as foo1, bar} from '...'; - declarations = extractSymbolsFromNamedExportStatement(node, declarations); - } - // if the module name in the from clause cant be resolved to actual module location, - // just extract symbols listed in the exportClause for named exports, put them in variables first, as - // they will be categorized later using map argument. - else if (node.exportClause && ts.isNamedExports(node.exportClause)) { - node.exportClause.elements.forEach(exportSpecifier => { - declarations.variables.push(exportSpecifier.name.escapedText.toString()); - }); - } - // handles the case when exporting * from a module whose location can't be resolved - else { - console.log( - `The public API extraction of ${moduleName} is not complete, because it re-exports from ${moduleName} using * export but we couldn't resolve ${moduleName}` - ); + + const program = ts.createProgram([filePath], { + allowJs: true, + baseUrl: path.resolve(`${projectRoot}/node_modules`) + }); + const checker = program.getTypeChecker(); + const sourceFile = program.getSourceFile(filePath)!; + const module = checker.getSymbolAtLocation(sourceFile); + // no export from the file + if (!module) { + return exportDeclarations; } - return declarations; -} + const exports = checker.getExportsOfModule(module); -/** - * - * @param node compiler representation of a named export statement - * @param exportsFullList a list of all exported symbols retrieved from the location given in the export statement. - * - * This function filters on exportsFullList and keeps only those symbols that are listed in the given named export statement. - */ -function extractSymbolsFromNamedExportStatement( - node: ts.ExportDeclaration, - exportsFullList: MemberList -): MemberList { - if (node.exportClause && ts.isNamedExports(node.exportClause)) { - const actualExports: string[] = []; - node.exportClause.elements.forEach(exportSpecifier => { - const reExportedSymbol: string = extractOriginalSymbolName( - exportSpecifier - ); - // eg: export {foo as foo1 } from '...'; - // if export is renamed, replace with new name - // reExportedSymbol: stores the original symbol name - // exportSpecifier.name: stores the renamed symbol name - if (isExportRenamed(exportSpecifier)) { - actualExports.push(exportSpecifier.name.escapedText.toString()); - // reExportsMember stores all re-exported symbols in its orignal name. However, these re-exported symbols - // could be renamed by the re-export. We want to show the renamed name of the symbols in the final analysis report. - // Therefore, replaceAll simply replaces the original name of the symbol with the new name defined in re-export. - replaceAll( - exportsFullList, - reExportedSymbol, - exportSpecifier.name.escapedText.toString() - ); - } else { - actualExports.push(reExportedSymbol); - } - }); - // for named exports: requires a filter step which keeps only the symbols listed in the export statement. - filterAllBy(exportsFullList, actualExports); - } - return exportsFullList; -} -/** - * @param node compiler representation of a named export statement - * @param importSymbolCurrentNameToModuleLocation a map with imported symbol current name as key and the resolved module location as value. (map is populated by parsing import statements) - * @param importSymbolCurrentNameToOriginalName as imported symbols can be renamed, this map stores imported symbols current name and original name as key value pairs. - * @param importModuleLocationToExportedSymbolsList a map that maps module location to a list of its exported symbols. - * @param namespaceImportSymbolSet a set of namespace import symbols. - * @param parentDeclarations a list of exported symbols extracted from the module so far - * This function exclusively handles named export statements that has no from clause, i.e: statements like export {LogLevel}; - * first case: namespace export - * example: import * as fs from 'fs'; export {fs}; - * The function checks if namespaceImportSymbolSet has a namespace import symbol that of the same name, append the symbol to declarations.variables if exists. - * - * second case: import then export - * example: import {a} from '...'; export {a} - * The function retrieves the location where the exported symbol is defined from the corresponding import statements. - * - * third case: declare first then export - * examples: declare const apps: Map; export { apps }; - * function foo(){} ; export {foo as bar}; - * The function parses export clause of the statement and replaces symbol with its current name (if the symbol is renamed) from the declaration argument. - */ -function handleExportStatementsWithoutFromClause( - node: ts.ExportDeclaration, - importSymbolCurrentNameToModuleLocation: Map, - importSymbolCurrentNameToOriginalName: Map, - importModuleLocationToExportedSymbolsList: Map, - namespaceImportSymbolSet: Set, - parentDeclarations: MemberList -): void { - if (node.exportClause && ts.isNamedExports(node.exportClause)) { - node.exportClause.elements.forEach(exportSpecifier => { - // export symbol could be renamed, we retrieve both its current/renamed name and original name - const exportSymbolCurrentName = exportSpecifier.name.escapedText.toString(); - const exportSymbolOriginalName = extractOriginalSymbolName( - exportSpecifier - ); - // import * as fs from 'fs'; export {fs}; - if (namespaceImportSymbolSet.has(exportSymbolOriginalName)) { - parentDeclarations.variables.push(exportSymbolOriginalName); - replaceAll( - parentDeclarations, - exportSymbolOriginalName, - exportSymbolCurrentName - ); - } - // handles import then exports - // import {a as A , b as B} from '...' - // export {A as AA , B as BB }; - else if ( - importSymbolCurrentNameToModuleLocation.has(exportSymbolOriginalName) - ) { - const moduleLocation: string = importSymbolCurrentNameToModuleLocation.get( - exportSymbolOriginalName - )!; - let reExportedSymbols: MemberList; - if (importModuleLocationToExportedSymbolsList.has(moduleLocation)) { - reExportedSymbols = deepCopy( - importModuleLocationToExportedSymbolsList.get(moduleLocation)! - ); - } else { - reExportedSymbols = extractDeclarations( - importSymbolCurrentNameToModuleLocation.get( - exportSymbolOriginalName - )! - ); - importModuleLocationToExportedSymbolsList.set( - moduleLocation, - deepCopy(reExportedSymbols) - ); - } - let nameToBeReplaced = exportSymbolOriginalName; - // if current exported symbol is renamed in import clause. then we retrieve its original name from - // importSymbolCurrentNameToOriginalName map - if ( - importSymbolCurrentNameToOriginalName.has(exportSymbolOriginalName) - ) { - nameToBeReplaced = importSymbolCurrentNameToOriginalName.get( - exportSymbolOriginalName - )!; - } + for (const expt of exports) { + // get the source declaration where we can determine the type of the export. e.g. class vs function + let sourceSymbol = expt; + if (sourceSymbol.declarations[0].kind === ts.SyntaxKind.ExportSpecifier) { + sourceSymbol = checker.getAliasedSymbol(expt); + } - filterAllBy(reExportedSymbols, [nameToBeReplaced]); - // replace with new name - replaceAll( - reExportedSymbols, - nameToBeReplaced, - exportSymbolCurrentName - ); - - // concatenate re-exported MemberList with MemberList of the dts file - for (const key of Object.keys(parentDeclarations) as Array< - keyof MemberList - >) { - parentDeclarations[key].push(...reExportedSymbols[key]); - } - } - // handles declare first then export - // declare const apps: Map; - // export { apps as apps1}; - // function a() {}; - // export {a}; - else { - if (isExportRenamed(exportSpecifier)) { - replaceAll( - parentDeclarations, - exportSymbolOriginalName, - exportSymbolCurrentName - ); - } + if (!sourceSymbol.declarations || sourceSymbol.declarations.length === 0) { + console.log('Could not find the source symbol for ', expt.name); + continue; + } + const sourceDeclaration = sourceSymbol.declarations[0]; + + if (ts.isFunctionDeclaration(sourceDeclaration)) { + exportDeclarations.functions.push(expt.name); + } else if (ts.isClassDeclaration(sourceDeclaration)) { + exportDeclarations.classes.push(expt.name); + } else if (ts.isVariableDeclaration(sourceDeclaration)) { + exportDeclarations.variables.push(expt.name); + } else if (ts.isEnumDeclaration(sourceDeclaration)) { + // `const enum`s should not be analyzed. They do not add to bundle size and + // creating a file that imports them causes an error during the rollup step. + if ( + // Identifies if this enum had a "const" modifier attached. + !sourceDeclaration.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ConstKeyword) + ) { + exportDeclarations.enums.push(expt.name); } - }); + } else { + console.log(`export of unknown type: ${expt.name}`); + exportDeclarations.unknown.push(expt.name); + } } + + Object.values(exportDeclarations).forEach(each => { + each.sort(); + }); + + return exportDeclarations; } /** @@ -572,7 +321,8 @@ export function mapSymbolToType( functions: [], classes: [], variables: [], - enums: [] + enums: [], + unknown: [] }; for (const key of Object.keys(memberList) as Array) { @@ -587,23 +337,6 @@ export function mapSymbolToType( return newMemberList; } -function extractOriginalSymbolName( - exportSpecifier: ts.ExportSpecifier -): string { - // if symbol is renamed, then exportSpecifier.propertyName is not null and stores the orignal name, exportSpecifier.name stores the renamed name. - // if symbol is not renamed, then exportSpecifier.propertyName is null, exportSpecifier.name stores the orignal name. - if (exportSpecifier.propertyName) { - return exportSpecifier.propertyName.escapedText.toString(); - } - return exportSpecifier.name.escapedText.toString(); -} - -function filterAllBy(memberList: MemberList, keep: string[]): void { - for (const key of Object.keys(memberList) as Array) { - memberList[key] = memberList[key].filter(each => keep.includes(each)); - } -} - export function replaceAll( memberList: MemberList, original: string, @@ -630,10 +363,6 @@ function replaceWith( return rv; } -function isExportRenamed(exportSpecifier: ts.ExportSpecifier): boolean { - return exportSpecifier.propertyName != null; -} - /** * * This functions writes generated json report(s) to a file @@ -787,21 +516,6 @@ function retrieveBundleFileLocation(pkgJson: { } return ''; } -/** - * - * This function creates a map from a MemberList object which maps symbol names (key) listed - * to its type (value) - */ -export function buildMap(api: MemberList): Map { - const map: Map = new Map(); - - for (const type of Object.keys(api) as Array) { - api[type].forEach((element: string) => { - map.set(element, type); - }); - } - return map; -} /** * A recursive function that locates and generates reports for sub-modules @@ -833,7 +547,7 @@ async function traverseDirs( fs.lstatSync(p).isDirectory() && fs.existsSync(`${p}/package.json`) && JSON.parse(fs.readFileSync(`${p}/package.json`, { encoding: 'utf-8' }))[ - generateSizeAnalysisReportPkgJsonField + generateSizeAnalysisReportPkgJsonField ] ) { const subModuleReports: Report[] = await traverseDirs( @@ -860,8 +574,7 @@ async function traverseDirs( export async function buildJsonReport( moduleName: string, publicApi: MemberList, - jsFile: string, - map: Map + jsFile: string ): Promise { const result: Report = { name: moduleName, @@ -869,7 +582,7 @@ export async function buildJsonReport( }; for (const exp of publicApi.classes) { try { - result.symbols.push(await extractDependenciesAndSize(exp, jsFile, map)); + result.symbols.push(await extractDependenciesAndSize(exp, jsFile)); } catch (e) { console.log(e); } @@ -877,14 +590,14 @@ export async function buildJsonReport( for (const exp of publicApi.functions) { try { - result.symbols.push(await extractDependenciesAndSize(exp, jsFile, map)); + result.symbols.push(await extractDependenciesAndSize(exp, jsFile)); } catch (e) { console.log(e); } } for (const exp of publicApi.variables) { try { - result.symbols.push(await extractDependenciesAndSize(exp, jsFile, map)); + result.symbols.push(await extractDependenciesAndSize(exp, jsFile)); } catch (e) { console.log(e); } @@ -892,7 +605,7 @@ export async function buildJsonReport( for (const exp of publicApi.enums) { try { - result.symbols.push(await extractDependenciesAndSize(exp, jsFile, map)); + result.symbols.push(await extractDependenciesAndSize(exp, jsFile)); } catch (e) { console.log(e); } @@ -920,9 +633,9 @@ export async function generateReport( throw new Error(ErrorCode.INPUT_BUNDLE_FILE_DOES_NOT_EXIST); } - const publicAPI = extractDeclarations(resolvedDtsFile); - const map: Map = buildMap(publicAPI); - return buildJsonReport(name, publicAPI, bundleFile, map); + console.log('generating report for ', name); + const publicAPI = extractExports(resolvedBundleFile); + return buildJsonReport(name, publicAPI, bundleFile); } /** diff --git a/repo-scripts/size-analysis/bundle-analysis.ts b/repo-scripts/size-analysis/bundle-analysis.ts index 2e51db1c8eb..dd90acda074 100644 --- a/repo-scripts/size-analysis/bundle-analysis.ts +++ b/repo-scripts/size-analysis/bundle-analysis.ts @@ -22,7 +22,7 @@ import { bundleWithRollup } from './bundle/rollup'; import { bundleWithWebpack } from './bundle/webpack'; import { calculateContentSize } from './util'; import { minify } from './bundle/minify'; -import { extractDeclarations, MemberList } from './analysis-helper'; +import { extractAllTopLevelSymbols, MemberList } from './analysis-helper'; interface BundleAnalysisArgs { input: string; @@ -392,7 +392,7 @@ async function analyzeBundleWithBundler( analysisResult.debugInfo = { pathToBundle: bundleFilePath, pathToMinifiedBundle: minifiedBundleFilePath, - dependencies: extractDeclarations(bundleFilePath) + dependencies: extractAllTopLevelSymbols(bundleFilePath) }; } diff --git a/repo-scripts/size-analysis/cli.ts b/repo-scripts/size-analysis/cli.ts index 87987315f45..4e7b3a282fc 100644 --- a/repo-scripts/size-analysis/cli.ts +++ b/repo-scripts/size-analysis/cli.ts @@ -29,7 +29,7 @@ yargs type: 'array', alias: 'im', desc: - 'The name of the module(s) to be analyzed. example: --inputModule "@firebase/functions-exp" "firebase/auth-exp"' + 'The name of the module(s) to be analyzed. example: --inputModule "@firebase/functions" "firebase/auth"' }, inputDtsFile: { type: 'string', @@ -90,4 +90,4 @@ yargs // eslint-disable-next-line @typescript-eslint/no-explicit-any argv => runBundleAnalysis(argv as any) ) - .help().argv; + .help().parseSync(); diff --git a/repo-scripts/size-analysis/package-analysis.ts b/repo-scripts/size-analysis/package-analysis.ts index f6ebb78baae..2adedf6a27f 100644 --- a/repo-scripts/size-analysis/package-analysis.ts +++ b/repo-scripts/size-analysis/package-analysis.ts @@ -73,6 +73,11 @@ export async function analyzePackageSize( `${projectRoot}/packages/*` ]); allModulesLocation = allModulesLocation.filter(path => { + const pkgJsonPath = `${path}/package.json`; + if (!fs.existsSync(pkgJsonPath)){ + return false; + } + const json = JSON.parse( fs.readFileSync(`${path}/package.json`, { encoding: 'utf-8' }) ); diff --git a/repo-scripts/size-analysis/rollup.config.js b/repo-scripts/size-analysis/rollup.config.js index f4c4bb79ab6..505596e3d2e 100644 --- a/repo-scripts/size-analysis/rollup.config.js +++ b/repo-scripts/size-analysis/rollup.config.js @@ -24,7 +24,7 @@ const deps = Object.keys( Object.assign({}, pkg.peerDependencies, pkg.dependencies) ); -const nodeInternals = ['fs', 'path']; +const nodeInternals = ['fs', 'path', 'util']; export default [ { diff --git a/repo-scripts/size-analysis/test/size-analysis.test.ts b/repo-scripts/size-analysis/test/size-analysis.test.ts index 98319d19b8a..45b83a852e3 100644 --- a/repo-scripts/size-analysis/test/size-analysis.test.ts +++ b/repo-scripts/size-analysis/test/size-analysis.test.ts @@ -18,7 +18,6 @@ import { expect } from 'chai'; import { - extractDeclarations, MemberList, dedup, mapSymbolToType, @@ -27,8 +26,9 @@ import { ErrorCode, writeReportToDirectory, extractExternalDependencies, - buildMap, - Report + Report, + extractExports, + extractAllTopLevelSymbols } from '../analysis-helper'; import { @@ -39,14 +39,14 @@ import { import * as fs from 'fs'; import { resolve } from 'path'; -describe('extractDeclarations on .d.ts file', () => { +describe('extractExports', () => { let testModuleDtsFile: string; let extractedDeclarations: MemberList; before(() => { const start = Date.now(); testModuleDtsFile = getTestModuleDtsFilePath(); - extractedDeclarations = extractDeclarations(testModuleDtsFile); - console.log('extractDeclarations on .d.ts file took ', Date.now() - start); + extractedDeclarations = extractExports(testModuleDtsFile); + console.log('extractExports took ', Date.now() - start); }); // export {tar as tarr, tar1 as tarr1} from '..' it('test export rename', () => { @@ -187,42 +187,34 @@ describe('extractDeclarations on .d.ts file', () => { }); // import * as fs from 'fs' - // import * as tmp from 'tmp' - // export declare const aVar: tmp.FileOptions; // export { fs as fs1 }; it('test namespace export', () => { - expect(extractedDeclarations.variables).to.include.members(['fs1']); - expect(extractedDeclarations.variables).to.not.include.members(['tmp']); - expect(extractedDeclarations.variables).to.include.members(['aVar']); + expect(extractedDeclarations.unknown).to.include.members(['fs1']); }); }); -describe('extractDeclarations on js bundle file', () => { + +describe('extractAllTopLevelSymbols', () => { let subsetExportsBundleFile: string; let extractedDeclarations: MemberList; before(function () { - this.timeout(120000); const start = Date.now(); - const testModuleDtsFile: string = getTestModuleDtsFilePath(); - const map: Map = buildMap( - extractDeclarations(testModuleDtsFile) - ); subsetExportsBundleFile = getSubsetExportsBundleFilePath(); - extractedDeclarations = extractDeclarations(subsetExportsBundleFile, map); + extractedDeclarations = extractAllTopLevelSymbols(subsetExportsBundleFile); console.log( 'extractDeclarations on js bundle file took ', Date.now() - start ); }); it('test variable extractions', () => { - const variablesArray = ['aVar', 'fs1']; + const variablesArray = ['aVar']; variablesArray.sort(); - expect(extractedDeclarations.variables).to.have.members(variablesArray); + expect(extractedDeclarations.variables).to.include.members(variablesArray); }); it('test functions extractions', () => { const functionsArray = [ 'tar', - 'tarr1', + 'tar1', 'basicFuncExportEnumDependencies', 'd1', 'd2', @@ -236,16 +228,15 @@ describe('extractDeclarations on js bundle file', () => { it('test enums extractions', () => { const enumsArray = [ 'BasicEnumExport', - 'LogLevel2', 'BasicEnumExportBar', 'BasicEnumExportFar' ]; enumsArray.sort(); - expect(extractedDeclarations.enums).to.have.members(enumsArray); + expect(extractedDeclarations.variables).to.include.members(enumsArray); }); it('test classes extractions', () => { - const classesArray = ['Logger1']; + const classesArray = ['BasicClassExport']; classesArray.sort(); expect(extractedDeclarations.classes).to.have.members(classesArray); }); @@ -257,7 +248,8 @@ describe('test dedup helper function', () => { functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'], classes: ['aClass', 'bClass', 'aClass', 'cClass'], variables: ['aVar', 'bVar', 'cVar', 'aVar'], - enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'] + enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'], + unknown: [] }; memberList = dedup(memberList); @@ -287,7 +279,8 @@ describe('test dedup helper function', () => { functions: [], classes: [], variables: ['aVar', 'bVar', 'cVar', 'aVar'], - enums: [] + enums: [], + unknown: [] }; memberList = dedup(memberList); expect(memberList.functions).to.have.length(0); @@ -308,7 +301,8 @@ describe('test replaceAll helper function', () => { functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'], classes: ['aClass', 'bClass', 'aClass', 'cClass'], variables: ['aVar', 'bVar', 'cVar', 'aVar'], - enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'] + enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'], + unknown: [] }; const original: string = 'aFunc'; const replaceTo: string = 'replacedFunc'; @@ -331,7 +325,8 @@ describe('test replaceAll helper function', () => { functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'], classes: ['aClass', 'bClass', 'aClass', 'cClass'], variables: ['aVar', 'bVar', 'cVar', 'aVar'], - enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'] + enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'], + unknown: [] }; const replaceTo: string = 'replacedClass'; const original: string = 'bClass'; @@ -354,7 +349,8 @@ describe('test replaceAll helper function', () => { functions: ['aFunc', 'aFunc', 'bFunc', 'cFunc'], classes: ['aClass', 'bClass', 'aClass', 'cClass'], variables: ['aVar', 'bVar', 'cVar', 'aVar'], - enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'] + enums: ['aEnum', 'bEnum', 'cEnum', 'dEnum'], + unknown: [] }; const replaceTo: string = 'replacedEnum'; const original: string = 'eEnum'; @@ -377,7 +373,8 @@ describe('test mapSymbolToType helper function', () => { functions: ['aVar', 'bFunc', 'cFunc'], classes: ['bClass', 'cClass'], variables: ['aClass', 'bVar', 'cVar', 'aEnum'], - enums: ['bEnum', 'cEnum', 'dEnum', 'aFunc'] + enums: ['bEnum', 'cEnum', 'dEnum', 'aFunc'], + unknown: [] }; const map: Map = new Map([ diff --git a/repo-scripts/size-analysis/test/test-inputs/subsetExports.ts b/repo-scripts/size-analysis/test/test-inputs/subsetExports.ts index 4398d9d2616..dac88c80ea2 100644 --- a/repo-scripts/size-analysis/test/test-inputs/subsetExports.ts +++ b/repo-scripts/size-analysis/test/test-inputs/subsetExports.ts @@ -14,13 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - export { aVar, LogLevel2, - Logger1, tarr1, - fs1, basicFuncExportEnumDependencies, - basicFuncExportFuncDependenciesBar + basicFuncExportFuncDependenciesBar, + BasicClassExport } from './index'; diff --git a/repo-scripts/size-analysis/tsconfig.json b/repo-scripts/size-analysis/tsconfig.json index 01d19282ef7..326e95a0fa6 100644 --- a/repo-scripts/size-analysis/tsconfig.json +++ b/repo-scripts/size-analysis/tsconfig.json @@ -5,7 +5,7 @@ "module": "commonjs", "moduleResolution": "node", "resolveJsonModule": true, - "target": "es5", + "target": "es2017", "esModuleInterop": true, "declaration": true, "strict": true diff --git a/scripts/size_report/report_binary_size.ts b/scripts/size_report/report_binary_size.ts index f2589630735..d453f49462a 100644 --- a/scripts/size_report/report_binary_size.ts +++ b/scripts/size_report/report_binary_size.ts @@ -45,18 +45,21 @@ function generateReportForCDNScripts(): Report[] { const reports = []; const firebaseRoot = path.resolve(__dirname, '../../packages/firebase'); const pkgJson = require(`${firebaseRoot}/package.json`); + const compatPkgJson = require(`${firebaseRoot}/compat/package.json`); const special_files = [ - 'firebase-performance-standalone.es2017.js', - 'firebase-performance-standalone.js', - 'firebase-firestore.memory.js', - 'firebase.js' + 'firebase-performance-standalone-compat.es2017.js', + 'firebase-performance-standalone-compat.js', + 'firebase-compat.js' ]; const files = [ ...special_files.map((file: string) => `${firebaseRoot}/${file}`), ...pkgJson.components.map( - (component: string) => `${firebaseRoot}/firebase-${component}.js` + (component: string) => `${firebaseRoot}/firebase-${component.replace('/', '-')}.js` + ), + ...compatPkgJson.components.map( + (component: string) => `${firebaseRoot}/firebase-${component}-compat.js` ) ]; @@ -77,7 +80,16 @@ async function generateReportForNPMPackages(): Promise { for (const info of packageInfo) { const packages = await findAllPackages(info.location); for (const pkg of packages) { - reports.push(...(await collectBinarySize(pkg))); + try { + reports.push(...(await collectBinarySize(pkg))); + } catch (e) { + // log errors and continue to the next package + console.log( + `failed to generate report for ${pkg}. + error: ${e}` + ); + } + } } return reports; @@ -104,11 +116,12 @@ async function collectBinarySize(pkg: string): Promise { const fields = [ 'main', 'module', - 'esm2017', 'browser', + 'esm5', 'react-native', + 'cordova', 'lite', - 'lite-esm2017' + 'lite-esm5' ]; const json = require(pkg); for (const field of fields) { diff --git a/scripts/size_report/report_modular_export_binary_size.ts b/scripts/size_report/report_modular_export_binary_size.ts index 6fa245089c9..deb44a03d5b 100644 --- a/scripts/size_report/report_modular_export_binary_size.ts +++ b/scripts/size_report/report_modular_export_binary_size.ts @@ -27,6 +27,7 @@ import { RequestBody, RequestEndpoint } from './size_report_helper'; +import { existsSync } from 'fs'; interface ModularExportBinarySizeRequestBody extends RequestBody { modules: Report[]; } @@ -37,7 +38,12 @@ async function generateReport(): Promise { ]); allModulesLocation = allModulesLocation.filter(path => { - const json = require(`${path}/package.json`); + const pkgJsonPath = `${path}/package.json`; + if (!existsSync(pkgJsonPath)){ + return false; + } + + const json = require(pkgJsonPath); return ( json.name.startsWith('@firebase') && !json.name.includes('-compat') &&