diff --git a/index.js b/index.js index 56c18e6..1196bb6 100644 --- a/index.js +++ b/index.js @@ -28,7 +28,7 @@ server({ port: PORT }, [ }) } }), - get('/export-sizes', async ctx => { + get('/exports-sizes', async ctx => { const packageString = decodeURIComponent(ctx.query.p) try { diff --git a/src/getPackageExportSizes.ts b/src/getPackageExportSizes.ts index 9e56737..9d7a501 100644 --- a/src/getPackageExportSizes.ts +++ b/src/getPackageExportSizes.ts @@ -30,15 +30,15 @@ export async function getAllPackageExports( options: InstallPackageOptions = {} ) { const startTime = performance.now() - const { name: packageName, normalPath } = parsePackageString(packageString) + const { name: packageName, normalPath, importPath, isLocal } = parsePackageString(packageString) const installPath = await InstallationUtils.preparePath(packageName) try { - await installPackage(packageString, installPath, options) + await installPackage(normalPath, installPath, options) const results = await getAllExports( packageString, - normalPath || installPath, - packageName + isLocal ? normalPath : installPath, + importPath ) Telemetry.packageExports(packageString, startTime, true) return results @@ -57,16 +57,16 @@ export async function getPackageExportSizes( } ) { const startTime = performance.now() - const { name: packageName, normalPath } = parsePackageString(packageString) + const { name: packageName, normalPath, importPath, isLocal } = parsePackageString(packageString) const installPath = await InstallationUtils.preparePath(packageName) try { - await installPackage(packageString, installPath, options) + await installPackage(normalPath, installPath, options) const exportMap = await getAllExports( packageString, - normalPath || installPath, - packageName + isLocal ? normalPath : installPath, + importPath ) const exports = Object.keys(exportMap).filter(exp => !(exp === 'default')) @@ -77,6 +77,7 @@ export async function getPackageExportSizes( const builtDetails = await BuildUtils.buildPackageIgnoringMissingDeps({ name: packageName, installPath, + importPath, externals, options: { customImports: exports, diff --git a/src/getPackageStats.ts b/src/getPackageStats.ts index 641f605..88614ea 100644 --- a/src/getPackageStats.ts +++ b/src/getPackageStats.ts @@ -60,14 +60,14 @@ export default async function getPackageStats( ...optionsRaw, } - const { name: packageName, isLocal } = parsePackageString(packageString) + const { name: packageName, isLocal, normalPath, importPath } = parsePackageString(packageString) const installPath = await InstallationUtils.preparePath(packageName) if (options.debug) { console.log('Install path:', installPath) } try { - await InstallationUtils.installPackage(packageString, installPath, { + await InstallationUtils.installPackage(normalPath, installPath, { isLocal, client: options.client, limitConcurrency: options.limitConcurrency, @@ -76,10 +76,11 @@ export default async function getPackageStats( }) const externals = getExternals(packageName, installPath) - const [pacakgeJSONDetails, builtDetails] = await Promise.all([ + const [packageJSONDetails, builtDetails] = await Promise.all([ getPackageJSONDetails(packageName, installPath), BuildUtils.buildPackageIgnoringMissingDeps({ name: packageName, + importPath, installPath, externals, options: { @@ -91,18 +92,25 @@ export default async function getPackageStats( }), ]) - const hasCSSAsset = builtDetails.assets.some(asset => asset.type === 'css') - const mainAsset = builtDetails.assets.find( - asset => - asset.name === 'main' && asset.type === (hasCSSAsset ? 'css' : 'js') - ) + const mainAssets = builtDetails.assets.filter(asset => asset.name === 'main') - if (!mainAsset) { + if (!mainAssets.length) { throw new UnexpectedBuildError( 'Did not find a main asset in the built bundle' ) } + const mainSizes = mainAssets.reduce((acc, asset) => { + acc.size += asset.size + acc.gzip += asset.gzip + if (asset.parse) { + acc.parse = acc.parse || {baseParseTime: 0, scriptParseTime: 0} + acc.parse.baseParseTime += asset.parse.baseParseTime || 0 + acc.parse.scriptParseTime += asset.parse.scriptParseTime || 0 + } + return acc + }, { size: 0, gzip: 0, parse: null as null | { baseParseTime: number, scriptParseTime: number } }) + Telemetry.packageStats( packageString, true, @@ -110,11 +118,11 @@ export default async function getPackageStats( options ) return { - ...pacakgeJSONDetails, + ...packageJSONDetails, ...builtDetails, - size: mainAsset.size, - gzip: mainAsset.gzip, - parse: mainAsset.parse, + size: mainSizes.size, + gzip: mainSizes.gzip, + parse: mainSizes.parse, } } catch (e) { Telemetry.packageStats( diff --git a/src/utils/build.utils.ts b/src/utils/build.utils.ts index 29f49c2..133dfe7 100644 --- a/src/utils/build.utils.ts +++ b/src/utils/build.utils.ts @@ -25,6 +25,7 @@ import { CreateEntryPointOptions, } from '../common.types' import Telemetry from './telemetry.utils' +import { resolve } from './exports.utils' type CompilePackageArgs = { name: string @@ -43,6 +44,7 @@ type CompilePackageReturn = { type BuildPackageArgs = { name: string installPath: string + importPath: string, externals: Externals options: BuildPackageOptions } @@ -51,7 +53,7 @@ type WebpackStatsAsset = NonNullable[0] const BuildUtils = { createEntryPoint( - packageName: string, + importPath: string, installPath: string, options: CreateEntryPointOptions ) { @@ -65,23 +67,24 @@ const BuildUtils = { if (options.esm) { if (options.customImports) { importStatement = ` - import { ${options.customImports.join(', ')} } from '${packageName}'; + import { ${options.customImports.join(', ')} } from '${importPath}'; console.log(${options.customImports.join(', ')}) ` } else { - importStatement = `import p from '${packageName}'; console.log(p)` + importStatement = `import p from '${importPath}'; console.log(p)` } } else { if (options.customImports) { importStatement = ` const { ${options.customImports.join( ', ' - )} } = require('${packageName}'); + )} } = await import(/* webpackMode: 'eager' */'${importPath}'); console.log(${options.customImports.join(', ')}) ` } else { - importStatement = `const p = require('${packageName}'); console.log(p)` + importStatement = `const p = await import(/* webpackMode: 'eager' */'${importPath}'); console.log(p)` } + importStatement = `;(async () => { ${importStatement} })();` } try { @@ -177,6 +180,7 @@ const BuildUtils = { async buildPackage({ name, installPath, + importPath, externals, options, }: BuildPackageArgs) { @@ -187,14 +191,14 @@ const BuildUtils = { return { assets: [] } } options.customImports.forEach(importt => { - entry[importt] = BuildUtils.createEntryPoint(name, installPath, { + entry[importt] = BuildUtils.createEntryPoint(importPath, installPath, { customImports: [importt], entryFilename: importt, esm: true, }) }) } else { - entry['main'] = BuildUtils.createEntryPoint(name, installPath, { + entry['main'] = BuildUtils.createEntryPoint(importPath, installPath, { esm: false, customImports: options.customImports, }) @@ -330,16 +334,20 @@ const BuildUtils = { name, externals, installPath, + importPath, options, }: BuildPackageArgs) { const buildStartTime = performance.now() let buildIteration = 1 + importPath = await resolve(installPath, importPath) + try { const buildResult = await BuildUtils.buildPackage({ name, externals, installPath, + importPath, options, }) Telemetry.buildPackage(name, true, buildStartTime, { @@ -367,6 +375,7 @@ const BuildUtils = { const rebuiltResult = await BuildUtils.buildPackage({ name, externals: newExternals, + importPath, installPath, options, }) diff --git a/src/utils/common.utils.ts b/src/utils/common.utils.ts index b1ead9e..a5f26f3 100644 --- a/src/utils/common.utils.ts +++ b/src/utils/common.utils.ts @@ -3,6 +3,7 @@ import path from 'path' import builtInModules from 'builtin-modules' import fs from 'fs' import os from 'os' +import { last } from 'lodash' const homeDirectory = os.homedir() @@ -92,8 +93,9 @@ type ParsePackageResult = { name: string version: string | null scoped: boolean + importPath: string isLocal?: boolean - normalPath?: string + normalPath: string } function parseLocalPackageString(packageString: string): ParsePackageResult { @@ -104,6 +106,7 @@ function parseLocalPackageString(packageString: string): ParsePackageResult { name: packageJSON.name, version: packageJSON.version, scoped: packageJSON.name.startsWith('@'), + importPath: packageString, normalPath: packageString, isLocal: true, } @@ -111,26 +114,55 @@ function parseLocalPackageString(packageString: string): ParsePackageResult { function parseScopedPackageString(packageString: string): ParsePackageResult { const lastAtIndex = packageString.lastIndexOf('@') + const firstSlashIndex = packageString.indexOf('/') + const secondSlashIndex = packageString.indexOf('/', firstSlashIndex + 1) + + const name = lastAtIndex === 0 + ? secondSlashIndex === -1 + ? packageString + : packageString.substring(0, secondSlashIndex) + : packageString.substring(0, lastAtIndex) + const version = lastAtIndex === 0 + ? null + : secondSlashIndex === -1 + ? packageString.substring(lastAtIndex + 1) + : packageString.substring(lastAtIndex + 1, secondSlashIndex) + const path = secondSlashIndex === -1 + ? null + : packageString.substring(secondSlashIndex + 1) + return { - name: - lastAtIndex === 0 - ? packageString - : packageString.substring(0, lastAtIndex), - version: - lastAtIndex === 0 ? null : packageString.substring(lastAtIndex + 1), + name, + importPath: name + (path ? '/' + path : ''), + version, + normalPath: name + (version ? '@' + version : ''), scoped: true, } } function parseUnscopedPackageString(packageString: string): ParsePackageResult { const lastAtIndex = packageString.lastIndexOf('@') + const firstSlashIndex = packageString.indexOf('/') + + const name = lastAtIndex === -1 + ? firstSlashIndex === -1 + ? packageString + : packageString.substring(0, firstSlashIndex) + : packageString.substring(0, lastAtIndex) + const version = lastAtIndex === -1 + ? null + : firstSlashIndex === -1 + ? packageString.substring(lastAtIndex + 1) + : packageString.substring(lastAtIndex + 1, firstSlashIndex) + const path = firstSlashIndex === -1 + ? null + : packageString.substring(firstSlashIndex + 1) + return { - name: - lastAtIndex === -1 - ? packageString - : packageString.substring(0, lastAtIndex), - version: - lastAtIndex === -1 ? null : packageString.substring(lastAtIndex + 1), + name, + importPath: name + (path ? '/' + path : ''), + version, + normalPath: name + (version ? '@' + version : ''), scoped: false, } } diff --git a/src/utils/exports.utils.ts b/src/utils/exports.utils.ts index 9fa49dc..d2d29da 100644 --- a/src/utils/exports.utils.ts +++ b/src/utils/exports.utils.ts @@ -223,10 +223,11 @@ const resolver = enhancedResolve.create({ modules: webpackConfig?.resolve?.modules, // @ts-ignore Error due to unsynced types for enhanced resolve and webpack mainFields: webpackConfig?.resolve?.mainFields, + conditionNames: ['import', 'require', 'node', 'browser', 'default'] }) -const resolve = async (context: string, path: string): Promise => - new Promise((resolve, reject) => { +export function resolve (context: string, path: string): Promise { + return new Promise((resolve, reject) => { resolver(context, path, (err: Error, result: string) => { if (err) { reject(err) @@ -235,6 +236,7 @@ const resolve = async (context: string, path: string): Promise => } }) }) +} type ResolvedExports = { [key: string]: string