-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
V1.5: experimental
esbuild
& change/improvements to hydration. (#191)
* feat: 🎸 move hydration to be wrapped in requestIdleCallback * chore: 🤖 v bump * ci: 🎡 make linter happy * v1.5 feature: Esbuild for Dev Mode (#187) * feat: 🎸 modularize rollup plugin for esbuild prep * feat: 🎸 esbuild working with rollup plugin * test: 💍 updating tests * test: 💍 trim defunct tests * test: 💍 make linter happy * fix: 🐛 fix type that made dynamic a required key * feat: 🎸 only bundle components for the client * try/catch and sourcemap writing * fix: 🐛 add check for sourcemap before writing * v bump * fix: 🐛 support plugins with templates in node_modules * chore: 🤖 v bump * feat: 🎸 allow for plugins to use custom layouts * chore: 🤖 bump before @next release * ci: 🎡 fix the linter complaining Co-authored-by: Nick Reese <[email protected]> * fix: 🐛 change css rebasing relative to distDir not distElder * fix: 🐛 handle esbuild server race condition * fix: 🐛 check for components in rollup plugin to prevent failure * chore: 🤖 v bump * refactor: 💡 rename conflicting variable in getRollupConfig * feat: 🎸 PoC of prop compression * remove devalue * feat: 🎸 prop compression toggling and debugging in elder.config * chore: 🤖 v bump * fix: 🐛 add back in devalue as it is used by rollup * fix: 🐛 devalue to package.lock * refactor: 💡 remove stray console log * fix: 🐛 fix rollup file naming from using indexes * chore: 🤖 v bump * fix: 🐛 Fix race condition causing undefined client components * feat: 🎸 faster prop compression for the client * fix: 🐛 much, much faster prop compression * fix: 🐛 prop compression * fix: 🐛 less buggy prop compression with new Map() * chore: 🤖 remove commented code * feat: 🎸 Allow for external file props * fix: 🐛 Fix promise.all() call and misnamed variable * chore: 🤖 v bump * test: 💍 extract propCompression and add tests * test: 💍 tests for hydrate components * test: 💍 finish tests ✅ Closes: gp * ci: 🎡 fix ci * fix: 🐛 windowsPathFix for prop files * feat: 🎸 move hydration logic to the browser * fix: 🐛 fix hydration bug * chore: 🤖 v bump * test: 💍 update tests * feat: 🎸 less hydration code * chore: 🤖 make linter happy * fix: 🐛 fix failing tests * fix: 🐛 add semicolons to end of if statements to prevent issues * fix: 🐛 1 more semi colon * chore: 🤖 vbump * test: 💍 update test to include semi colons * test: 💍 extract out makeDynamicPermalinkFn * chore: 🤖 upgrade packages and remove unused * fix: 🐛 downgrade svelte due to unnamed import error * chore: 🤖 remove console log * chore: 🤖 downgrade svelte to stable * chore: 🤖 bump svelte, remove console log, update tests * ci: 🎡 make prettier have warings to prevent reformatting * chore: 🤖 run eslint fix on prettier after solving spacing issue Co-authored-by: Nick Reese <[email protected]>
- Loading branch information
Showing
49 changed files
with
5,485 additions
and
4,262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,6 @@ | |
"semi": true, | ||
"trailingComma": "all", | ||
"singleQuote": true, | ||
"printWidth": 120 | ||
"printWidth": 120, | ||
"tabWidth": 2 | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
/* eslint-disable import/no-dynamic-require */ | ||
// reload the build process when svelte files are added clearing the cache. | ||
|
||
// server that reloads the app which watches the file system for changes. | ||
// reload can also be called after esbuild finishes the rebuild. | ||
// the file watcher should restart the entire esbuild process when a new svelte file is seen. This includes clearing caches. | ||
|
||
import { build, BuildResult, buildSync } from 'esbuild'; | ||
import glob from 'glob'; | ||
import path from 'path'; | ||
|
||
import fs from 'fs-extra'; | ||
|
||
// eslint-disable-next-line import/no-unresolved | ||
import { PreprocessorGroup } from 'svelte/types/compiler/preprocess/types'; | ||
import esbuildPluginSvelte from './esbuildPluginSvelte'; | ||
import { InitializationOptions, SettingsOptions } from '../utils/types'; | ||
import { getElderConfig } from '..'; | ||
import { devServer } from '../rollup/rollupPlugin'; | ||
import getPluginLocations from '../utils/getPluginLocations'; | ||
|
||
const production = process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'PRODUCTION'; | ||
export type TPreprocess = PreprocessorGroup | PreprocessorGroup[] | false; | ||
export type TSvelteHandler = { | ||
config: SettingsOptions; | ||
preprocess: TPreprocess; | ||
}; | ||
|
||
export function getSvelteConfig(elderConfig: SettingsOptions): TPreprocess { | ||
const svelteConfigPath = path.resolve(elderConfig.rootDir, `./svelte.config.js`); | ||
if (fs.existsSync(svelteConfigPath)) { | ||
try { | ||
// eslint-disable-next-line import/no-dynamic-require | ||
const req = require(svelteConfigPath); | ||
if (req) { | ||
return req; | ||
} | ||
} catch (err) { | ||
if (err.code === 'MODULE_NOT_FOUND') { | ||
console.warn(`Unable to load svelte.config.js from ${svelteConfigPath}`, err); | ||
} | ||
return false; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
export function getPackagesWithSvelte(pkg, elderConfig: SettingsOptions) { | ||
const pkgs = [] | ||
.concat(pkg.dependents ? Object.keys(pkg.dependents) : []) | ||
.concat(pkg.devDependencies ? Object.keys(pkg.devDependencies) : []); | ||
const sveltePackages = pkgs.reduce((out, cv) => { | ||
try { | ||
const resolved = path.resolve(elderConfig.rootDir, `./node_modules/${cv}/package.json`); | ||
const current = require(resolved); | ||
if (current.svelte) { | ||
out.push(cv); | ||
} | ||
} catch (e) { | ||
// | ||
} | ||
return out; | ||
}, []); | ||
return sveltePackages; | ||
} | ||
|
||
const getRestartHelper = (startOrRestartServer) => { | ||
let state; | ||
const defaultState = { ssr: false, client: false }; | ||
const resetState = () => { | ||
state = JSON.parse(JSON.stringify(defaultState)); | ||
}; | ||
|
||
resetState(); | ||
|
||
// eslint-disable-next-line consistent-return | ||
return (type: 'start' | 'reset' | 'client' | 'ssr') => { | ||
if (type === 'start') { | ||
return startOrRestartServer(); | ||
} | ||
if (type === 'reset') { | ||
return resetState(); | ||
} | ||
|
||
state[type] = true; | ||
if (state.ssr && state.client) { | ||
startOrRestartServer(); | ||
resetState(); | ||
} | ||
}; | ||
}; | ||
|
||
const svelteHandler = async ({ elderConfig, svelteConfig, replacements, restartHelper }) => { | ||
const builders: { ssr?: BuildResult; client?: BuildResult } = {}; | ||
|
||
// eslint-disable-next-line global-require | ||
const pkg = require(path.resolve(elderConfig.rootDir, './package.json')); | ||
const globPath = path.resolve(elderConfig.rootDir, `./src/**/*.svelte`); | ||
const initialEntryPoints = glob.sync(globPath); | ||
const sveltePackages = getPackagesWithSvelte(pkg, elderConfig); | ||
const elderPlugins = getPluginLocations(elderConfig); | ||
|
||
builders.ssr = await build({ | ||
entryPoints: [...initialEntryPoints, ...elderPlugins.files], | ||
bundle: true, | ||
outdir: elderConfig.$$internal.ssrComponents, | ||
plugins: [ | ||
esbuildPluginSvelte({ | ||
type: 'ssr', | ||
sveltePackages, | ||
elderConfig, | ||
svelteConfig, | ||
}), | ||
], | ||
watch: { | ||
onRebuild(error) { | ||
restartHelper('ssr'); | ||
if (error) console.error('ssr watch build failed:', error); | ||
}, | ||
}, | ||
format: 'cjs', | ||
target: ['node12'], | ||
platform: 'node', | ||
sourcemap: !production, | ||
minify: production, | ||
outbase: 'src', | ||
external: pkg.dependents ? [...Object.keys(pkg.dependents)] : [], | ||
define: { | ||
'process.env.componentType': "'server'", | ||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), | ||
...replacements, | ||
}, | ||
}); | ||
|
||
builders.client = await build({ | ||
entryPoints: [...initialEntryPoints.filter((i) => i.includes('src/components')), ...elderPlugins.files], | ||
bundle: true, | ||
outdir: elderConfig.$$internal.clientComponents, | ||
entryNames: '[dir]/[name].[hash]', | ||
plugins: [ | ||
esbuildPluginSvelte({ | ||
type: 'client', | ||
sveltePackages, | ||
elderConfig, | ||
svelteConfig, | ||
}), | ||
], | ||
watch: { | ||
onRebuild(error) { | ||
if (error) console.error('client watch build failed:', error); | ||
restartHelper('client'); | ||
}, | ||
}, | ||
format: 'esm', | ||
target: ['es2020'], | ||
platform: 'node', | ||
sourcemap: !production, | ||
minify: true, | ||
splitting: true, | ||
chunkNames: 'chunks/[name].[hash]', | ||
logLevel: 'error', | ||
outbase: 'src', | ||
define: { | ||
'process.env.componentType': "'browser'", | ||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), | ||
...replacements, | ||
}, | ||
}); | ||
|
||
restartHelper('start'); | ||
|
||
const restart = async () => { | ||
if (builders.ssr) await builders.ssr.stop(); | ||
if (builders.client) await builders.client.stop(); | ||
restartHelper('reset'); | ||
return svelteHandler({ | ||
elderConfig, | ||
svelteConfig, | ||
replacements, | ||
restartHelper, | ||
}); | ||
}; | ||
|
||
return restart; | ||
}; | ||
|
||
type TEsbuildBundler = { | ||
initializationOptions?: InitializationOptions; | ||
replacements?: { [key: string]: string | boolean }; | ||
}; | ||
|
||
const esbuildBundler = async ({ initializationOptions = {}, replacements = {} }: TEsbuildBundler = {}) => { | ||
const elderConfig = getElderConfig(initializationOptions); | ||
const svelteConfig = getSvelteConfig(elderConfig); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const { startOrRestartServer, startWatcher, childProcess } = devServer({ | ||
elderConfig, | ||
}); | ||
|
||
const restartHelper = getRestartHelper(startOrRestartServer); | ||
|
||
if (!fs.existsSync(path.resolve('./node_modules/intersection-observer/intersection-observer.js'))) { | ||
throw new Error(`Missing 'intersection-observer' dependency. Run 'npm i --save intersection-observer' to fix.`); | ||
} | ||
|
||
buildSync({ | ||
format: 'iife', | ||
minify: true, | ||
watch: false, | ||
outfile: path.resolve( | ||
elderConfig.prefix ? path.join(elderConfig.distDir, elderConfig.prefix) : elderConfig.distDir, | ||
`./static/intersection-observer.js`, | ||
), | ||
entryPoints: [path.resolve('./node_modules/intersection-observer/intersection-observer.js')], | ||
}); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const restartEsbuild = await svelteHandler({ | ||
elderConfig, | ||
svelteConfig, | ||
replacements, | ||
restartHelper, | ||
}); | ||
|
||
startWatcher(); | ||
}; | ||
export default esbuildBundler; |
Oops, something went wrong.