Skip to content

Commit

Permalink
feat: add support for parsing Flow, applied to RN modules by default
Browse files Browse the repository at this point in the history
  • Loading branch information
ambar committed Feb 8, 2024
1 parent 7368d49 commit 516cae7
Show file tree
Hide file tree
Showing 15 changed files with 1,360 additions and 1,248 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"ignoreChanges": [
"**/fixtures/**",
"**/__tests__/**",
"**/__snapshots__/**",
"**/test/**",
"**/*.md"
],
"npmClient": "yarn",
"useWorkspaces": true,
"conventionalCommits": true
}
22 changes: 10 additions & 12 deletions measure-bundle-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ yarn add measure-bundle-size
```ts
import {measureIterable, measure, type MeasureResult} from 'measure-bundle-size'

type MeasureOptions = {
debug?: boolean
log?: (...args: any[]) => void
stats?: boolean | 'tree' | 'table'
workspaceFolder?: string
flowPattern?: RegExp
}

// Lazy async generator API
type measureIterable = (input: string, fileName?: string | null, {
debug?: boolean
log?: (...args: any[]) => void
stats?: boolean | 'tree' | 'table'
workspaceFolder?: string
}) => AsyncGenerator<MeasureResult>
type measureIterable = (input: string, fileName?: string | null, MeasureOptions) => AsyncGenerator<MeasureResult>

for await (const result of measureIterable(`code`, __filename, {
debug: true,
Expand All @@ -33,12 +36,7 @@ for await (const result of measureIterable(`code`, __filename, {
}

// Promise API
type measure = (input: string, fileName?: string | null, {
debug?: boolean
log?: (...args: any[]) => void
stats?: boolean | 'tree' | 'table'
workspaceFolder?: string
}) => Promise<MeasureResult[]>
type measure = (input: string, fileName?: string | null, MeasureOptions) => Promise<MeasureResult[]>

const results = await measure(`code`, __filename, {
debug: true,
Expand Down
5 changes: 4 additions & 1 deletion measure-bundle-size/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,23 @@
"@babel/traverse": "^7.23.6",
"bytes": "^3.1.0",
"debug": "^4.3.2",
"escalade": "^3.1.1"
"escalade": "^3.1.1",
"sucrase": "^3.35.0"
},
"peerDependencies": {
"esbuild": "^0"
},
"devDependencies": {
"@ambarli/alias": "^0.0.0",
"@react-native/assets": "^1.0.0",
"@types/bytes": "^3.1.1",
"@types/debug": "^4.1.7",
"@types/dedent": "^0.7.0",
"@types/lodash": "^4.14.176",
"@types/react": "^17.0.32",
"date-fns": "^2.17.0",
"dedent": "^0.7.0",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-inline-center": "^1.0.1",
"sanitize.css": "^13.0.0",
Expand Down
64 changes: 61 additions & 3 deletions measure-bundle-size/src/measure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ const peerExternalPlugin: esbuild.Plugin = {
},
}

const stripFlowTypesPlugin = (
pattern = /\/node_modules\/(@react-native|react-native|react-native-linear-gradient)\/(.*)\.js$/
): esbuild.Plugin => {
return {
name: 'flow',
setup(build) {
build.onLoad({filter: pattern}, async (args) => {
const source = await fs.readFile(args.path, 'utf8')
const {stripFlowTypes} = await import('./stripFlowTypes')
const output = stripFlowTypes(source.toString(), args.path)
const contents = output ?? ''
return {
contents,
loader: 'jsx',
}
})
},
}
}

type StatsOpt = boolean | 'tree' | 'table'

type BundleResult = {
Expand Down Expand Up @@ -128,19 +148,26 @@ const pickPkgFields = (pkg: Pkg) => {
])
}

/**
* TODO: add more support for RN
* - add exports support: `conditions: ['react-native', 'import']`
* - use jsx loader for .js
*/
const bundle = async (
statement: string,
importInfo: ImportInfo,
{
baseDir,
projectPkgFile,
flowPattern,
stats: statsOpt = false,
cache: cacheOpt = false,
}: {
baseDir: string
projectPkgFile?: string | void
stats?: StatsOpt
cache?: boolean
flowPattern?: RegExp
}
): Promise<BundleResult> => {
const bundleMark = timeMark<'bundle' | 'zip' | 'analyze'>()
Expand All @@ -164,6 +191,7 @@ const bundle = async (
return {} as T
})

// TODO: use current package path as the cache key
const cacheKey =
cacheOpt && rootPkg !== undefined && genCacheKey(rootPkg, entryInput)
if (cacheOpt && cacheKey) {
Expand Down Expand Up @@ -196,9 +224,35 @@ const bundle = async (
},
outfile: '<bundle>.js',
absWorkingDir: workingDir,
plugins: [builtinExternalPlugin, peerExternalPlugin],
plugins: [
builtinExternalPlugin,
peerExternalPlugin,
stripFlowTypesPlugin(flowPattern),
],
resolveExtensions: [
// RN
'.ios.js',
'.android.js',
'.native.js',
// node
'.mjs',
'.cjs',
// defaults to .jsx,.js,.tsx,.ts,.css,.json
'.jsx',
'.js',
'.tsx',
'.ts',
'.css',
'.json',
],
loader: {
'.node': 'binary',
// RN or web app may use static assets
'.jpeg': 'empty',
'.jpg': 'empty',
'.png': 'empty',
'.webp': 'empty',
'.gif': 'empty',
},
external,
target: 'esnext',
Expand Down Expand Up @@ -300,6 +354,10 @@ type MeasureOptions = {
* the path of the workspace folder
*/
workspaceFolder?: string
/**
* the files pattern to strip flow types
*/
flowPattern?: RegExp
}

/**
Expand All @@ -308,7 +366,7 @@ type MeasureOptions = {
export async function* measureIterable(
input: string,
fileName?: string | null,
{debug, log, stats, cache, workspaceFolder}: MeasureOptions = {}
{debug, log, stats, cache, workspaceFolder, flowPattern}: MeasureOptions = {}
): AsyncGenerator<MeasureResult> {
if (debug) {
logger.enabled = true
Expand All @@ -335,7 +393,7 @@ export async function* measureIterable(
parseMark.end('parse')
logger(parseMark.print())

const bundleOpts = {baseDir, projectPkgFile, stats, cache}
const bundleOpts = {baseDir, projectPkgFile, stats, cache, flowPattern}
for (const importInfo of result.imports) {
const statement = input.substring(importInfo.start, importInfo.end)
// await new Promise((resolve) => setTimeout(resolve, 500))
Expand Down
20 changes: 20 additions & 0 deletions measure-bundle-size/src/stripFlowTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {transform} from 'sucrase'

/**
* Strip flow types from the given source code.
*
* Why sucrase is used instead of babel, bundle size of this package:
* - `sucrase`: 848kb -> 1.1mb
* - `@babel/core` (with `@babel/plugin-transform-flow-strip-types`): 848kb -> 5.7mb
*/
export function stripFlowTypes(source: string, filename?: string) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
const result = transform(source, {
transforms: ['flow', 'jsx', 'imports'],
jsxRuntime: 'automatic',
production: true,
filePath: filename,
})
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
return result.code
}
Loading

0 comments on commit 516cae7

Please sign in to comment.