Skip to content

Commit

Permalink
feat: support flow, apply to react native modules by default
Browse files Browse the repository at this point in the history
  • Loading branch information
ambar committed Feb 7, 2024
1 parent 7368d49 commit beaf1b9
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 40 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
4 changes: 3 additions & 1 deletion measure-bundle-size/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
"@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",
Expand Down
62 changes: 59 additions & 3 deletions measure-bundle-size/src/measure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,28 @@ 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) => {
console.info(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 @@ -134,13 +156,15 @@ const bundle = async (
{
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 +188,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 +221,36 @@ const bundle = async (
},
outfile: '<bundle>.js',
absWorkingDir: workingDir,
plugins: [builtinExternalPlugin, peerExternalPlugin],
plugins: [
builtinExternalPlugin,
peerExternalPlugin,
stripFlowTypesPlugin(flowPattern),
],
conditions: ['production'],
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 +352,10 @@ type MeasureOptions = {
* the path of the workspace folder
*/
workspaceFolder?: string
/**
* the files pattern to strip flow types
*/
flowPattern?: RegExp
}

/**
Expand All @@ -308,7 +364,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 +391,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
}
36 changes: 22 additions & 14 deletions measure-bundle-size/test/__snapshots__/measure.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Array [
"result": Object {
"human": Object {
"size": "2.13KB",
"zippedSize": "923B",
"zippedSize": "930B",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -91,7 +91,7 @@ Array [
"pkgFile": "node_modules/sanitize.css/package.json",
"size": 2185,
"stats": undefined,
"zippedSize": 923,
"zippedSize": 930,
},
},
Object {
Expand All @@ -104,7 +104,7 @@ Array [
"result": Object {
"human": Object {
"size": "525B",
"zippedSize": "355B",
"zippedSize": "353B",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -119,7 +119,7 @@ Array [
"pkgFile": "node_modules/sanitize.css/package.json",
"size": 525,
"stats": undefined,
"zippedSize": 355,
"zippedSize": 353,
},
},
]
Expand Down Expand Up @@ -295,7 +295,7 @@ Array [
"result": Object {
"human": Object {
"size": "2.43KB",
"zippedSize": "1.14KB",
"zippedSize": "1.13KB",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -310,7 +310,7 @@ Array [
"pkgFile": "node_modules/date-fns/package.json",
"size": 2489,
"stats": undefined,
"zippedSize": 1166,
"zippedSize": 1162,
},
},
]
Expand All @@ -333,7 +333,7 @@ Array [
"result": Object {
"human": Object {
"size": "71.78KB",
"zippedSize": "26.18KB",
"zippedSize": "26.02KB",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -348,7 +348,7 @@ Array [
"pkgFile": "node_modules/lodash/package.json",
"size": 73500,
"stats": undefined,
"zippedSize": 26804,
"zippedSize": 26641,
},
},
]
Expand All @@ -371,7 +371,7 @@ Array [
"result": Object {
"human": Object {
"size": "7.79KB",
"zippedSize": "3.25KB",
"zippedSize": "3.26KB",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -389,7 +389,7 @@ Array [
"pkgFile": "node_modules/react/package.json",
"size": 7974,
"stats": undefined,
"zippedSize": 3326,
"zippedSize": 3335,
},
},
]
Expand All @@ -412,7 +412,7 @@ Array [
"result": Object {
"human": Object {
"size": "637B",
"zippedSize": "373B",
"zippedSize": "372B",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -427,7 +427,7 @@ Array [
"pkgFile": "node_modules/@ambarli/alias/package.json",
"size": 637,
"stats": undefined,
"zippedSize": 373,
"zippedSize": 372,
},
},
]
Expand All @@ -450,7 +450,7 @@ Array [
"result": Object {
"human": Object {
"size": "21.1KB",
"zippedSize": "8.33KB",
"zippedSize": "8.29KB",
},
"modulePkg": undefined,
"modulePkgFile": undefined,
Expand All @@ -465,7 +465,7 @@ Array [
"pkgFile": "node_modules/lodash/package.json",
"size": 21611,
"stats": undefined,
"zippedSize": 8526,
"zippedSize": 8489,
},
},
]
Expand Down Expand Up @@ -522,3 +522,11 @@ exports[`no package.json in exports 1`] = `
| \`<bundle>.js\` | | 308B | 100% |
| \`↪ escalade\` | dist/index.mjs | 285B | 92.5% |"
`;
exports[`parse flow type 1`] = `
"| Input | Files | Size | Percent |
| --- | --- | --- | --- |
| \`<bundle>.js\` | | 764B | 100% |
| \`↪ @react-native/assets\` | registry.js | 142B | 18.6% |
| \`↪ <import>\` | | 13B | 1.7% |"
`;
16 changes: 16 additions & 0 deletions measure-bundle-size/test/__snapshots__/stripFlowTypes.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`stripFlowTypes 1`] = `
"\\"use strict\\";var _jsxruntime = require(\\"react/jsx-runtime\\");Object.defineProperty(exports, \\"__esModule\\", {value: true});
const countries = {
US: \\"United States\\",
IT: \\"Italy\\",
FR: \\"France\\"
};
const italy = 'IT'; exports.italy = italy;
const foo = _jsxruntime.jsx.call(void 0, 'div', {} ); exports.foo = foo
"
`;
12 changes: 12 additions & 0 deletions measure-bundle-size/test/measure.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,15 @@ test('no package.json in exports', async () => {
})
expect(r.result!.stats).toMatchSnapshot()
})

test('parse flow type', async () => {
const [r] = await measure(
`import {registerAsset} from '@react-native/assets/registry'`,
null,
{
workspaceFolder: __dirname,
stats: 'table',
}
)
expect(r.result!.stats).toMatchSnapshot()
})
17 changes: 17 additions & 0 deletions measure-bundle-size/test/stripFlowTypes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {stripFlowTypes} from '../src/stripFlowTypes';

test('stripFlowTypes', () => {
const code = `
const countries = {
US: "United States",
IT: "Italy",
FR: "France"
};
type Country = $Keys<typeof countries>;
export const italy: Country = 'IT';
export const foo = <div />
`
expect(stripFlowTypes(code)).toMatchSnapshot()
})
Loading

0 comments on commit beaf1b9

Please sign in to comment.