master 4df4c2af6ac2 cached
10 files
14.8 KB
4.3k tokens
12 symbols
1 requests
Download .txt
Repository: halfnelson/svelte-it-will-scale
Branch: master
Commit: 4df4c2af6ac2
Files: 10
Total size: 14.8 KB

Directory structure:
gitextract_8fw9pmo2/

├── .gitignore
├── README.md
├── build-chart.js
├── build-chart.preact.js
├── build-chart.react.js
├── makeindexs-react.js
├── makeindexs.js
├── package.json
├── rollup.config.js
└── rollup.config.react.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/node_modules/
/public/build/

.DS_Store
index-1.js
index-5.js
index-10.js
index-20.js
index-30.js
index-40.js
index-50.js
index-60.js
index-70.js
index-1-react.js
index-5-react.js
index-10-react.js
index-20-react.js
index-30-react.js
index-40-react.js
index-50-react.js


================================================
FILE: README.md
================================================
# Will it Scale? - Finding Svelte's Inflection Point

from [#2546](https://github.com/sveltejs/svelte/issues/2546)
> Theoretically, yes there is an inflection point. Where that inflection point is is the only thing that really matters. In practice, you're unlikely to hit that inflection point on any given page of your app, as long as you're using code-splitting (which Sapper gives you OOTB)

We can work out this inflection point given sufficient data.

**TLDR: Inflection is at 120 KB of component source (for react 16)**

## Finding the Svelte Bundle Size Formula

To find the inflection point, we need to determine the relationship between a projects svelte component source, and its generated bundle size.

We use the components from: 
 * [Svelte Website](https://github.com/sveltejs/svelte/tree/master/site/src)
 * [Svelte Realworld](https://github.com/sveltejs/realworld/)
 * [Svelte HN](https://github.com/sveltejs/hn.svelte.dev)

After removing the style tags, and bundling and minification we get a graph:

![Svelte source vs bundle size](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Source%20size%20vs%20Bundle%20size%20-%20Svelte.svg)

The yellow line is our final zipped and minified bundle, each data point is a bundle built with rollup by the scripts in this repository.

We can see the relationship is mostly linear, and a linear regression gives 

`Svelte Bundle Bytes = 0.493 * Source_Size + 2811`

## Finding the React Bundle Size Formula

We will compare our Svelte bundle size formula with reacts.

We will use the react components from:
 * [React Native Website](https://github.com/facebook/react-native-website/)
 * [React Redux Realworld](https://github.com/gothinkster/react-redux-realworld-example-app)
 * [Builder Book](https://github.com/builderbook/builderbook)

After ensuring that we only include react components and not util/library code, we end up with the following graph:
![React source vs bundle size](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Source%20size%20vs%20Bundle%20size%20-%20React.svg)

The lines certainly look flatter than Svelte's, so we have a good chance of finding an inflection point. Linear regression on our data determines the relationship between React component source code and gzipped minified bundle size to be:

`React Bundle Bytes = 0.153 * Source_Size + 43503`

## Calculating the Inflection Point

With our two bundle size formulas, we are able to find an inflection point using basic algebra.... but that isn't as pretty as graphs:

![Source size vs Bundle Size Comparison](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Source%20size%20vs%20Bundle%20size.svg)

Examining the graph shows __the inflection point is about 120KB of component source__.


## What does it mean?

It would seem that at about 120KB, the size advantage of going with a compiler over a runtime has vanished. 

Should this be a concern? 

from [#2546](https://github.com/sveltejs/svelte/issues/2546)
> In practice, you're unlikely to hit that inflection point on any given page of your app, 
as long as you're using code-splitting (which Sapper gives you OOTB)

We should be able to check this assertion by looking at the amount of svelte code in each of the projects we used:

![Svelte Project Size Examples](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Project%20Component%20Source%20Size.svg)

It turns out that svelte projects tend to be lean already. None of the projects we used came close to the inflection point, even all three projects combined falls short (we get just over half way there).

If these projects didn't come close, the odds are most projects won't. Indeed, even the figures above ignore code splitting, which would reduce the first payload significantly.

## Summary

It is good to have an answer to "Will it Scale", and we can be assured that yes "It Will Scale"


================================================
FILE: build-chart.js
================================================

const childProcess = require('child_process')
const fs = require('fs')

const component_counts = [1,5,10,20,30,40,50,60,70]

// build the bundles
console.log("building bundles....");

childProcess.execSync('npx rollup -c');

console.log("performing gzips...")
// build the gzips
component_counts.forEach(c => 
    childProcess.execSync(`gzip -c public/build/bundle-${c}--lib-min.js > public/build/bundle-${c}--lib-min.js.gz`)
);

function getTotalScriptSize(index) {
    const content = fs.readFileSync(index, 'utf-8');

    let size = 0;

    content.replace(/from '(.*?)'/gmi, (match, fname) => {
        const content = fs.readFileSync(fname, 'utf-8');
        let non_style_content = content.replace(/<style>.*?<\/style>/mgis, sub => {
            return '';
        })
        size = size + non_style_content.length;
        return match;
    })
    return size;
}

// print the stats
function fileSize(fname) {
    return fs.statSync(fname).size
}

console.log('count original lib-nomin nolib-nomin lib-min nolib-min lib-min-gz')

component_counts.forEach(c => {
    const bp = `public/build/bundle-${c}-`
    console.log([c,
         getTotalScriptSize(`index-${c}.js`),
         fileSize(`${bp}-lib-nomin.js`),
         fileSize(`${bp}-nolib-nomin.js`),
         fileSize(`${bp}-lib-min.js`),
         fileSize(`${bp}-nolib-min.js`), 
         fileSize(`${bp}-lib-min.js.gz`)
        ].join(' '));
})










================================================
FILE: build-chart.preact.js
================================================

const childProcess = require('child_process')
const fs = require('fs')

const component_counts = [1,5,10,20,30,40,50]

// build the bundles
console.log("building bundles....");

childProcess.execSync('npx rollup -c rollup.config.react.js');

console.log("performing gzips...")
// build the gzips
component_counts.forEach(c => 
    childProcess.execSync(`tar -czvf public/build/bundle-react-${c}--lib-min.tar.gz public/build/bundle-react-${c}--min.js node_modules/preact/hooks/dist/hooks.umd.js node_modules/preact/dist/preact.umd.js`)
);


function getTotalScriptSize(index) {
    let size = 0;
    content = fs.readFileSync(index, "utf-8")
    content.replace(/from '(.*?)'/gmi, (match, fname) => {
        size = size + fileSize(fname)
        return match;
    })
    return size;
}

// print the stats
function fileSize(fname) {
    return fs.statSync(fname).size
}

console.log('count original lib-nomin nolib-nomin lib-min nolib-min lib-min-gz')

react_lib_size = fileSize('node_modules/preact/hooks/dist/hooks.umd.js') + fileSize('node_modules/preact/dist/preact.umd.js')

component_counts.forEach(c => {
    const bp = `public/build/bundle-react-${c}-`
    console.log([c,
         getTotalScriptSize(`index-${c}-react.js`),
         fileSize(`${bp}-nomin.js`) + react_lib_size,
         fileSize(`${bp}-nomin.js`),
         fileSize(`${bp}-min.js`) + react_lib_size,
         fileSize(`${bp}-min.js`),
         fileSize(`${bp}-lib-min.tar.gz`)
        ].join(' '));
})










================================================
FILE: build-chart.react.js
================================================

const childProcess = require('child_process')
const fs = require('fs')

const component_counts = [1,5,10,20,30,40,50]

// build the bundles
console.log("building bundles....");

childProcess.execSync('npx rollup -c rollup.config.react.js');

console.log("performing gzips...")
// build the gzips
component_counts.forEach(c => 
    childProcess.execSync(`tar -czvf public/build/bundle-react-${c}--lib-min.tar.gz public/build/bundle-react-${c}--min.js node_modules/react/umd/react.production.min.js node_modules/react-dom/umd/react-dom.production.min.js`)
);


function getTotalScriptSize(index) {
    let size = 0;
    content = fs.readFileSync(index, "utf-8")
    content.replace(/from '(.*?)'/gmi, (match, fname) => {
        size = size + fileSize(fname)
        return match;
    })
    return size;
}

// print the stats
function fileSize(fname) {
    return fs.statSync(fname).size
}

console.log('count original lib-nomin nolib-nomin lib-min nolib-min lib-min-gz')

react_lib_size = fileSize('node_modules/react/umd/react.production.min.js') + fileSize('node_modules/react-dom/umd/react-dom.production.min.js')

component_counts.forEach(c => {
    const bp = `public/build/bundle-react-${c}-`
    console.log([c,
         getTotalScriptSize(`index-${c}-react.js`),
         fileSize(`${bp}-nomin.js`) + react_lib_size,
         fileSize(`${bp}-nomin.js`),
         fileSize(`${bp}-min.js`) + react_lib_size,
         fileSize(`${bp}-min.js`),
         fileSize(`${bp}-lib-min.tar.gz`)
        ].join(' '));
})










================================================
FILE: makeindexs-react.js
================================================
const fg = require('fast-glob')
const fs = require('fs')
const sizes = [1,5,10,20,30,40,50,60,70]
const dirs = ['../react-realworld/src/components', '../react-native-website/website/core', '../react-native-website/website/pages/en', '../builderbook/pages', '../builderbook/components']

let entries = fg.sync(dirs.map(dir => dir+'/**/*.js'))

function shuffleArray(array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}
function fileAsComponentName(fname) {
    return fname.replace('.js','').replace(/[\[\]\/\.\-@]/g, '_')
}

let out = []
shuffleArray(entries);

console.log('entries', entries)
for (let entry of entries) {
    out.push(`export { default as ${fileAsComponentName(entry)} } from '${entry}'`)
}

for (let count of sizes) {
    if (count > out.length) break;
    fs.writeFileSync(`index-${count}-react.js`, out.slice(0,count).join('\n'))
}




================================================
FILE: makeindexs.js
================================================
const fg = require('fast-glob')
const fs = require('fs')
const sizes = [1,5,10,20,30,40,50,60,70]
const dirs = ['../svelte/site/src', '../realworld', '../svelte-hn']

let entries = fg.sync(dirs.map(dir => dir+'/**/*.svelte'))

function shuffleArray(array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}
function fileAsComponentName(fname) {
    return fname.replace('.svelte','').replace(/[\[\]\/\.\-@]/g, '_')
}

let out = []
shuffleArray(entries);

for (let entry of entries) {
    out.push(`export { default as ${fileAsComponentName(entry)} } from '${entry}'`)
}

for (let count of sizes) {
    if (count > out.length) break;
    fs.writeFileSync(`index-${count}.js`, out.slice(0,count).join('\n'))
}




================================================
FILE: package.json
================================================
{
  "name": "svelte-app",
  "version": "1.0.0",
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public"
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^14.0.0",
    "@rollup/plugin-node-resolve": "^8.0.0",
    "rollup": "^2.3.4",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^7.0.0",
    "svelte": "^3.0.0"
  },
  "dependencies": {
    "@babel/core": "^7.11.4",
    "@babel/plugin-syntax-class-properties": "^7.10.4",
    "@babel/plugin-transform-react-jsx": "^7.10.4",
    "@babel/plugin-transform-runtime": "^7.11.0",
    "@babel/preset-react": "^7.10.4",
    "@rollup/plugin-babel": "^5.2.0",
    "fast-glob": "^3.2.4",
    "preact": "^10.4.7",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "sirv-cli": "^1.0.0"
  }
}


================================================
FILE: rollup.config.js
================================================
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
const fs = require('fs')
const path = require('path')

function genConfig(size, include_svelte_lib, minify) {
	let inp = `index-${size}.js`
	
	//ensure only the components specified are included.
	var content = fs.readFileSync(inp, 'utf-8');
	let components = []
	content.replace(/from '(.*?)'/gmi, (match, fname) => {
		components.push(path.resolve(__dirname, fname));
		return ''
	})
	
	return {
		input: inp,
		output: {
			format: 'esm',
			name: 'app',
			file: `public/build/bundle-${size}-${include_svelte_lib ? '-lib' : '-nolib'}${minify ? '-min' : '-nomin'}.js`
		},
		external: (id, parent, isResolved) => {
			if (id == 'svelte') return !include_svelte_lib;
			if (id.startsWith('svelte/')) return !include_svelte_lib;
			if (id.includes('svelte/internal')) return !include_svelte_lib;
			if (!id.startsWith('.')) return true;

			let abs = path.resolve(path.dirname(parent), id)

	   		return components.indexOf(abs) < 0;
		},
		plugins: [
			svelte({
				// enable run-time checks when not in production
				dev: false,
				// we'll extract any component CSS out into
				// a separate file - better for performance
				css: css => {
					css.write(`public/build/bundle-${size}.css`);
				}
			}),
	
			// If you have external dependencies installed from
			// npm, you'll most likely need these plugins. In
			// some cases you'll need additional configuration -
			// consult the documentation for details:
			// https://github.com/rollup/plugins/tree/master/packages/commonjs
			resolve({
				browser: true,
				dedupe: ['svelte']
			}),
			commonjs(),
			minify && terser()
		]
		
	};
}



let configs = []
let sizes = [1,5,10,20,30,40,50,60,70]
sizes.forEach(size => {
	configs = configs.concat([
		genConfig(size, false, false),
		genConfig(size, false, true),
		genConfig(size, true, false),
		genConfig(size, true, true)
	]);
});

export default configs


================================================
FILE: rollup.config.react.js
================================================
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
const fs = require('fs')
const path = require('path')

function genReactConfig(size, minify) {
	let inp = `index-${size}-react.js`
	
	//ensure only the components specified are included.
	var content = fs.readFileSync(inp, 'utf-8');
	let components = []
	content.replace(/from '(.*?)'/gmi, (match, fname) => {
		components.push(path.resolve(__dirname, fname));
		return ''
	})
	
	return {
		input: inp,
		output: {
			format: 'esm',
			name: 'app',
			file: `public/build/bundle-react-${size}-${minify ? '-min' : '-nomin'}.js`
		},
		external: (id, parent, isResolved) => {
			if (!id.startsWith('.')) return true;
			if (id.includes('@babel/runtime')) return true;
			let abs = path.resolve(path.dirname(parent), id)
	   		return components.indexOf(abs) < 0;
		},
		plugins: [
			babel({
				babelHelpers: "runtime",
				plugins: 
					["@babel/plugin-transform-runtime", "@babel/plugin-transform-react-jsx", "@babel/plugin-syntax-class-properties"]
				
			}),
			
			// If you have external dependencies installed from
			// npm, you'll most likely need these plugins. In
			// some cases you'll need additional configuration -
			// consult the documentation for details:
			// https://github.com/rollup/plugins/tree/master/packages/commonjs
			resolve({
				browser: true
			}),
			commonjs(),
			minify && terser()
		]
		
	};
}


let configs = []

let reactSizes = [1,5,10,20,30,40,50]
reactSizes.forEach(size => {
	configs = configs.concat([
		genReactConfig(size, false),
		genReactConfig(size, true),
	]);
});


export default configs
Download .txt
gitextract_8fw9pmo2/

├── .gitignore
├── README.md
├── build-chart.js
├── build-chart.preact.js
├── build-chart.react.js
├── makeindexs-react.js
├── makeindexs.js
├── package.json
├── rollup.config.js
└── rollup.config.react.js
Download .txt
SYMBOL INDEX (12 symbols across 7 files)

FILE: build-chart.js
  function getTotalScriptSize (line 18) | function getTotalScriptSize(index) {
  function fileSize (line 35) | function fileSize(fname) {

FILE: build-chart.preact.js
  function getTotalScriptSize (line 19) | function getTotalScriptSize(index) {
  function fileSize (line 30) | function fileSize(fname) {

FILE: build-chart.react.js
  function getTotalScriptSize (line 19) | function getTotalScriptSize(index) {
  function fileSize (line 30) | function fileSize(fname) {

FILE: makeindexs-react.js
  function shuffleArray (line 8) | function shuffleArray(array) {
  function fileAsComponentName (line 16) | function fileAsComponentName(fname) {

FILE: makeindexs.js
  function shuffleArray (line 8) | function shuffleArray(array) {
  function fileAsComponentName (line 16) | function fileAsComponentName(fname) {

FILE: rollup.config.js
  function genConfig (line 8) | function genConfig(size, include_svelte_lib, minify) {

FILE: rollup.config.react.js
  function genReactConfig (line 8) | function genReactConfig(size, minify) {
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
  {
    "path": ".gitignore",
    "chars": 271,
    "preview": "/node_modules/\n/public/build/\n\n.DS_Store\nindex-1.js\nindex-5.js\nindex-10.js\nindex-20.js\nindex-30.js\nindex-40.js\nindex-50."
  },
  {
    "path": "README.md",
    "chars": 3958,
    "preview": "# Will it Scale? - Finding Svelte's Inflection Point\n\nfrom [#2546](https://github.com/sveltejs/svelte/issues/2546)\n> The"
  },
  {
    "path": "build-chart.js",
    "chars": 1418,
    "preview": "\nconst childProcess = require('child_process')\nconst fs = require('fs')\n\nconst component_counts = [1,5,10,20,30,40,50,60"
  },
  {
    "path": "build-chart.preact.js",
    "chars": 1487,
    "preview": "\nconst childProcess = require('child_process')\nconst fs = require('fs')\n\nconst component_counts = [1,5,10,20,30,40,50]\n\n"
  },
  {
    "path": "build-chart.react.js",
    "chars": 1525,
    "preview": "\nconst childProcess = require('child_process')\nconst fs = require('fs')\n\nconst component_counts = [1,5,10,20,30,40,50]\n\n"
  },
  {
    "path": "makeindexs-react.js",
    "chars": 1004,
    "preview": "const fg = require('fast-glob')\nconst fs = require('fs')\nconst sizes = [1,5,10,20,30,40,50,60,70]\nconst dirs = ['../reac"
  },
  {
    "path": "makeindexs.js",
    "chars": 854,
    "preview": "const fg = require('fast-glob')\nconst fs = require('fs')\nconst sizes = [1,5,10,20,30,40,50,60,70]\nconst dirs = ['../svel"
  },
  {
    "path": "package.json",
    "chars": 807,
    "preview": "{\n  \"name\": \"svelte-app\",\n  \"version\": \"1.0.0\",\n  \"scripts\": {\n    \"build\": \"rollup -c\",\n    \"dev\": \"rollup -c -w\",\n    "
  },
  {
    "path": "rollup.config.js",
    "chars": 2064,
    "preview": "import svelte from 'rollup-plugin-svelte';\nimport resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rol"
  },
  {
    "path": "rollup.config.react.js",
    "chars": 1732,
    "preview": "import resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport babel from '@r"
  }
]

About this extraction

This page contains the full source code of the halfnelson/svelte-it-will-scale GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (14.8 KB), approximately 4.3k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!