[
  {
    "path": ".gitignore",
    "content": "/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.js\nindex-60.js\nindex-70.js\nindex-1-react.js\nindex-5-react.js\nindex-10-react.js\nindex-20-react.js\nindex-30-react.js\nindex-40-react.js\nindex-50-react.js\n"
  },
  {
    "path": "README.md",
    "content": "# Will it Scale? - Finding Svelte's Inflection Point\n\nfrom [#2546](https://github.com/sveltejs/svelte/issues/2546)\n> 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)\n\nWe can work out this inflection point given sufficient data.\n\n**TLDR: Inflection is at 120 KB of component source (for react 16)**\n\n## Finding the Svelte Bundle Size Formula\n\nTo find the inflection point, we need to determine the relationship between a projects svelte component source, and its generated bundle size.\n\nWe use the components from: \n * [Svelte Website](https://github.com/sveltejs/svelte/tree/master/site/src)\n * [Svelte Realworld](https://github.com/sveltejs/realworld/)\n * [Svelte HN](https://github.com/sveltejs/hn.svelte.dev)\n\nAfter removing the style tags, and bundling and minification we get a graph:\n\n![Svelte source vs bundle size](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Source%20size%20vs%20Bundle%20size%20-%20Svelte.svg)\n\nThe yellow line is our final zipped and minified bundle, each data point is a bundle built with rollup by the scripts in this repository.\n\nWe can see the relationship is mostly linear, and a linear regression gives \n\n`Svelte Bundle Bytes = 0.493 * Source_Size + 2811`\n\n## Finding the React Bundle Size Formula\n\nWe will compare our Svelte bundle size formula with reacts.\n\nWe will use the react components from:\n * [React Native Website](https://github.com/facebook/react-native-website/)\n * [React Redux Realworld](https://github.com/gothinkster/react-redux-realworld-example-app)\n * [Builder Book](https://github.com/builderbook/builderbook)\n\nAfter ensuring that we only include react components and not util/library code, we end up with the following graph:\n![React source vs bundle size](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Source%20size%20vs%20Bundle%20size%20-%20React.svg)\n\nThe 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:\n\n`React Bundle Bytes = 0.153 * Source_Size + 43503`\n\n## Calculating the Inflection Point\n\nWith our two bundle size formulas, we are able to find an inflection point using basic algebra.... but that isn't as pretty as graphs:\n\n![Source size vs Bundle Size Comparison](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Source%20size%20vs%20Bundle%20size.svg)\n\nExamining the graph shows __the inflection point is about 120KB of component source__.\n\n\n## What does it mean?\n\nIt would seem that at about 120KB, the size advantage of going with a compiler over a runtime has vanished. \n\nShould this be a concern? \n\nfrom [#2546](https://github.com/sveltejs/svelte/issues/2546)\n> In practice, you're unlikely to hit that inflection point on any given page of your app, \nas long as you're using code-splitting (which Sapper gives you OOTB)\n\nWe should be able to check this assertion by looking at the amount of svelte code in each of the projects we used:\n\n![Svelte Project Size Examples](https://raw.githubusercontent.com/halfnelson/svelte-it-will-scale/master/img/Project%20Component%20Source%20Size.svg)\n\nIt 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).\n\nIf 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.\n\n## Summary\n\nIt is good to have an answer to \"Will it Scale\", and we can be assured that yes \"It Will Scale\"\n"
  },
  {
    "path": "build-chart.js",
    "content": "\nconst childProcess = require('child_process')\nconst fs = require('fs')\n\nconst component_counts = [1,5,10,20,30,40,50,60,70]\n\n// build the bundles\nconsole.log(\"building bundles....\");\n\nchildProcess.execSync('npx rollup -c');\n\nconsole.log(\"performing gzips...\")\n// build the gzips\ncomponent_counts.forEach(c => \n    childProcess.execSync(`gzip -c public/build/bundle-${c}--lib-min.js > public/build/bundle-${c}--lib-min.js.gz`)\n);\n\nfunction getTotalScriptSize(index) {\n    const content = fs.readFileSync(index, 'utf-8');\n\n    let size = 0;\n\n    content.replace(/from '(.*?)'/gmi, (match, fname) => {\n        const content = fs.readFileSync(fname, 'utf-8');\n        let non_style_content = content.replace(/<style>.*?<\\/style>/mgis, sub => {\n            return '';\n        })\n        size = size + non_style_content.length;\n        return match;\n    })\n    return size;\n}\n\n// print the stats\nfunction fileSize(fname) {\n    return fs.statSync(fname).size\n}\n\nconsole.log('count original lib-nomin nolib-nomin lib-min nolib-min lib-min-gz')\n\ncomponent_counts.forEach(c => {\n    const bp = `public/build/bundle-${c}-`\n    console.log([c,\n         getTotalScriptSize(`index-${c}.js`),\n         fileSize(`${bp}-lib-nomin.js`),\n         fileSize(`${bp}-nolib-nomin.js`),\n         fileSize(`${bp}-lib-min.js`),\n         fileSize(`${bp}-nolib-min.js`), \n         fileSize(`${bp}-lib-min.js.gz`)\n        ].join(' '));\n})\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "build-chart.preact.js",
    "content": "\nconst childProcess = require('child_process')\nconst fs = require('fs')\n\nconst component_counts = [1,5,10,20,30,40,50]\n\n// build the bundles\nconsole.log(\"building bundles....\");\n\nchildProcess.execSync('npx rollup -c rollup.config.react.js');\n\nconsole.log(\"performing gzips...\")\n// build the gzips\ncomponent_counts.forEach(c => \n    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`)\n);\n\n\nfunction getTotalScriptSize(index) {\n    let size = 0;\n    content = fs.readFileSync(index, \"utf-8\")\n    content.replace(/from '(.*?)'/gmi, (match, fname) => {\n        size = size + fileSize(fname)\n        return match;\n    })\n    return size;\n}\n\n// print the stats\nfunction fileSize(fname) {\n    return fs.statSync(fname).size\n}\n\nconsole.log('count original lib-nomin nolib-nomin lib-min nolib-min lib-min-gz')\n\nreact_lib_size = fileSize('node_modules/preact/hooks/dist/hooks.umd.js') + fileSize('node_modules/preact/dist/preact.umd.js')\n\ncomponent_counts.forEach(c => {\n    const bp = `public/build/bundle-react-${c}-`\n    console.log([c,\n         getTotalScriptSize(`index-${c}-react.js`),\n         fileSize(`${bp}-nomin.js`) + react_lib_size,\n         fileSize(`${bp}-nomin.js`),\n         fileSize(`${bp}-min.js`) + react_lib_size,\n         fileSize(`${bp}-min.js`),\n         fileSize(`${bp}-lib-min.tar.gz`)\n        ].join(' '));\n})\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "build-chart.react.js",
    "content": "\nconst childProcess = require('child_process')\nconst fs = require('fs')\n\nconst component_counts = [1,5,10,20,30,40,50]\n\n// build the bundles\nconsole.log(\"building bundles....\");\n\nchildProcess.execSync('npx rollup -c rollup.config.react.js');\n\nconsole.log(\"performing gzips...\")\n// build the gzips\ncomponent_counts.forEach(c => \n    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`)\n);\n\n\nfunction getTotalScriptSize(index) {\n    let size = 0;\n    content = fs.readFileSync(index, \"utf-8\")\n    content.replace(/from '(.*?)'/gmi, (match, fname) => {\n        size = size + fileSize(fname)\n        return match;\n    })\n    return size;\n}\n\n// print the stats\nfunction fileSize(fname) {\n    return fs.statSync(fname).size\n}\n\nconsole.log('count original lib-nomin nolib-nomin lib-min nolib-min lib-min-gz')\n\nreact_lib_size = fileSize('node_modules/react/umd/react.production.min.js') + fileSize('node_modules/react-dom/umd/react-dom.production.min.js')\n\ncomponent_counts.forEach(c => {\n    const bp = `public/build/bundle-react-${c}-`\n    console.log([c,\n         getTotalScriptSize(`index-${c}-react.js`),\n         fileSize(`${bp}-nomin.js`) + react_lib_size,\n         fileSize(`${bp}-nomin.js`),\n         fileSize(`${bp}-min.js`) + react_lib_size,\n         fileSize(`${bp}-min.js`),\n         fileSize(`${bp}-lib-min.tar.gz`)\n        ].join(' '));\n})\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "makeindexs-react.js",
    "content": "const fg = require('fast-glob')\nconst fs = require('fs')\nconst sizes = [1,5,10,20,30,40,50,60,70]\nconst dirs = ['../react-realworld/src/components', '../react-native-website/website/core', '../react-native-website/website/pages/en', '../builderbook/pages', '../builderbook/components']\n\nlet entries = fg.sync(dirs.map(dir => dir+'/**/*.js'))\n\nfunction shuffleArray(array) {\n    for (var i = array.length - 1; i > 0; i--) {\n        var j = Math.floor(Math.random() * (i + 1));\n        var temp = array[i];\n        array[i] = array[j];\n        array[j] = temp;\n    }\n}\nfunction fileAsComponentName(fname) {\n    return fname.replace('.js','').replace(/[\\[\\]\\/\\.\\-@]/g, '_')\n}\n\nlet out = []\nshuffleArray(entries);\n\nconsole.log('entries', entries)\nfor (let entry of entries) {\n    out.push(`export { default as ${fileAsComponentName(entry)} } from '${entry}'`)\n}\n\nfor (let count of sizes) {\n    if (count > out.length) break;\n    fs.writeFileSync(`index-${count}-react.js`, out.slice(0,count).join('\\n'))\n}\n\n\n"
  },
  {
    "path": "makeindexs.js",
    "content": "const fg = require('fast-glob')\nconst fs = require('fs')\nconst sizes = [1,5,10,20,30,40,50,60,70]\nconst dirs = ['../svelte/site/src', '../realworld', '../svelte-hn']\n\nlet entries = fg.sync(dirs.map(dir => dir+'/**/*.svelte'))\n\nfunction shuffleArray(array) {\n    for (var i = array.length - 1; i > 0; i--) {\n        var j = Math.floor(Math.random() * (i + 1));\n        var temp = array[i];\n        array[i] = array[j];\n        array[j] = temp;\n    }\n}\nfunction fileAsComponentName(fname) {\n    return fname.replace('.svelte','').replace(/[\\[\\]\\/\\.\\-@]/g, '_')\n}\n\nlet out = []\nshuffleArray(entries);\n\nfor (let entry of entries) {\n    out.push(`export { default as ${fileAsComponentName(entry)} } from '${entry}'`)\n}\n\nfor (let count of sizes) {\n    if (count > out.length) break;\n    fs.writeFileSync(`index-${count}.js`, out.slice(0,count).join('\\n'))\n}\n\n\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"svelte-app\",\n  \"version\": \"1.0.0\",\n  \"scripts\": {\n    \"build\": \"rollup -c\",\n    \"dev\": \"rollup -c -w\",\n    \"start\": \"sirv public\"\n  },\n  \"devDependencies\": {\n    \"@rollup/plugin-commonjs\": \"^14.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^8.0.0\",\n    \"rollup\": \"^2.3.4\",\n    \"rollup-plugin-svelte\": \"^5.0.3\",\n    \"rollup-plugin-terser\": \"^7.0.0\",\n    \"svelte\": \"^3.0.0\"\n  },\n  \"dependencies\": {\n    \"@babel/core\": \"^7.11.4\",\n    \"@babel/plugin-syntax-class-properties\": \"^7.10.4\",\n    \"@babel/plugin-transform-react-jsx\": \"^7.10.4\",\n    \"@babel/plugin-transform-runtime\": \"^7.11.0\",\n    \"@babel/preset-react\": \"^7.10.4\",\n    \"@rollup/plugin-babel\": \"^5.2.0\",\n    \"fast-glob\": \"^3.2.4\",\n    \"preact\": \"^10.4.7\",\n    \"react\": \"^16.13.1\",\n    \"react-dom\": \"^16.13.1\",\n    \"sirv-cli\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import svelte from 'rollup-plugin-svelte';\nimport resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport { terser } from 'rollup-plugin-terser';\nconst fs = require('fs')\nconst path = require('path')\n\nfunction genConfig(size, include_svelte_lib, minify) {\n\tlet inp = `index-${size}.js`\n\t\n\t//ensure only the components specified are included.\n\tvar content = fs.readFileSync(inp, 'utf-8');\n\tlet components = []\n\tcontent.replace(/from '(.*?)'/gmi, (match, fname) => {\n\t\tcomponents.push(path.resolve(__dirname, fname));\n\t\treturn ''\n\t})\n\t\n\treturn {\n\t\tinput: inp,\n\t\toutput: {\n\t\t\tformat: 'esm',\n\t\t\tname: 'app',\n\t\t\tfile: `public/build/bundle-${size}-${include_svelte_lib ? '-lib' : '-nolib'}${minify ? '-min' : '-nomin'}.js`\n\t\t},\n\t\texternal: (id, parent, isResolved) => {\n\t\t\tif (id == 'svelte') return !include_svelte_lib;\n\t\t\tif (id.startsWith('svelte/')) return !include_svelte_lib;\n\t\t\tif (id.includes('svelte/internal')) return !include_svelte_lib;\n\t\t\tif (!id.startsWith('.')) return true;\n\n\t\t\tlet abs = path.resolve(path.dirname(parent), id)\n\n\t   \t\treturn components.indexOf(abs) < 0;\n\t\t},\n\t\tplugins: [\n\t\t\tsvelte({\n\t\t\t\t// enable run-time checks when not in production\n\t\t\t\tdev: false,\n\t\t\t\t// we'll extract any component CSS out into\n\t\t\t\t// a separate file - better for performance\n\t\t\t\tcss: css => {\n\t\t\t\t\tcss.write(`public/build/bundle-${size}.css`);\n\t\t\t\t}\n\t\t\t}),\n\t\n\t\t\t// If you have external dependencies installed from\n\t\t\t// npm, you'll most likely need these plugins. In\n\t\t\t// some cases you'll need additional configuration -\n\t\t\t// consult the documentation for details:\n\t\t\t// https://github.com/rollup/plugins/tree/master/packages/commonjs\n\t\t\tresolve({\n\t\t\t\tbrowser: true,\n\t\t\t\tdedupe: ['svelte']\n\t\t\t}),\n\t\t\tcommonjs(),\n\t\t\tminify && terser()\n\t\t]\n\t\t\n\t};\n}\n\n\n\nlet configs = []\nlet sizes = [1,5,10,20,30,40,50,60,70]\nsizes.forEach(size => {\n\tconfigs = configs.concat([\n\t\tgenConfig(size, false, false),\n\t\tgenConfig(size, false, true),\n\t\tgenConfig(size, true, false),\n\t\tgenConfig(size, true, true)\n\t]);\n});\n\nexport default configs\n"
  },
  {
    "path": "rollup.config.react.js",
    "content": "import resolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport babel from '@rollup/plugin-babel';\nimport { terser } from 'rollup-plugin-terser';\nconst fs = require('fs')\nconst path = require('path')\n\nfunction genReactConfig(size, minify) {\n\tlet inp = `index-${size}-react.js`\n\t\n\t//ensure only the components specified are included.\n\tvar content = fs.readFileSync(inp, 'utf-8');\n\tlet components = []\n\tcontent.replace(/from '(.*?)'/gmi, (match, fname) => {\n\t\tcomponents.push(path.resolve(__dirname, fname));\n\t\treturn ''\n\t})\n\t\n\treturn {\n\t\tinput: inp,\n\t\toutput: {\n\t\t\tformat: 'esm',\n\t\t\tname: 'app',\n\t\t\tfile: `public/build/bundle-react-${size}-${minify ? '-min' : '-nomin'}.js`\n\t\t},\n\t\texternal: (id, parent, isResolved) => {\n\t\t\tif (!id.startsWith('.')) return true;\n\t\t\tif (id.includes('@babel/runtime')) return true;\n\t\t\tlet abs = path.resolve(path.dirname(parent), id)\n\t   \t\treturn components.indexOf(abs) < 0;\n\t\t},\n\t\tplugins: [\n\t\t\tbabel({\n\t\t\t\tbabelHelpers: \"runtime\",\n\t\t\t\tplugins: \n\t\t\t\t\t[\"@babel/plugin-transform-runtime\", \"@babel/plugin-transform-react-jsx\", \"@babel/plugin-syntax-class-properties\"]\n\t\t\t\t\n\t\t\t}),\n\t\t\t\n\t\t\t// If you have external dependencies installed from\n\t\t\t// npm, you'll most likely need these plugins. In\n\t\t\t// some cases you'll need additional configuration -\n\t\t\t// consult the documentation for details:\n\t\t\t// https://github.com/rollup/plugins/tree/master/packages/commonjs\n\t\t\tresolve({\n\t\t\t\tbrowser: true\n\t\t\t}),\n\t\t\tcommonjs(),\n\t\t\tminify && terser()\n\t\t]\n\t\t\n\t};\n}\n\n\nlet configs = []\n\nlet reactSizes = [1,5,10,20,30,40,50]\nreactSizes.forEach(size => {\n\tconfigs = configs.concat([\n\t\tgenReactConfig(size, false),\n\t\tgenReactConfig(size, true),\n\t]);\n});\n\n\nexport default configs\n"
  }
]