Building multiple permutations of bundle
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 20.8k
- Forks
- 2.3k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 156
Description
Describe the problem
Concept
A permutation is a way to build multiples variants of the same Svelte application, according to various criteria.
A permutations will have an impact on the generated code by allowing certain files to be 'replaced' by others at build time.
This will allow to have a common codebase for multiple target, each of which could have its own specificities, like dedicated CSS/components/code/ressources/pages...
Some use-case :
- Sites for different audiences.
- Sites for different localisations.
- Sites for different clients, with specific template/customisation.
Describe the proposed solution
Permutations keys
Permutations would be defined by custom keys and their values, declared on the svelte.config.js
/// svelte.config.js
permutations: {
keys: {
/* any records as name: string[] */
}
},
Permutation keys will be project-specific key/values that define variants of our project.
For example, if we want to use a common project to build 3 specifics site/app for our customers (b2c), our partners (b2b), and our internal teams (int), we could define something like this :
/// svelte.config.js
permutations: {
keys: {
target: ['b2c', 'b2b', 'int']
}
},
And if, in addition to that, we want to generate 4 differents languages ('en', 'fr', 'de', 'it'), we could use :
/// svelte.config.js
permutations: {
keys: {
target: ['b2c', 'b2b', 'int'],
lang: ['en', 'fr', 'de', 'it' ]
}
},
We have now 3 targets variants in 4 differents langs, which will produce 12 distincts permutations builds :
- b2c,en
- b2c,fr
- b2c,de
- b2c,it
- b2b,en
- b2b,fr
- b2b,de
- b2b,it
- int,en
- int,fr
- int,de
- int,it
By default, each build will have his own subdirectory in the svelte-kit folder :
.svelte-kit/
b2c,en/
generated/
output/
types/
b2c,fr/
generated/
output/
types/
...
int,de/
generated/
output/
types/
int,it/
generated/
output/
types/
And all npm run command will use the first-permutation ('b2c,en' in the current case) :
# Launch dev mode for permutation 'b2c,en' (default)
npm run dev
But we might use a flag to select another permutation, for example --perm :
# Launch dev mode for permutation 'b2c' in 'fr' (explicitly)
npm run dev --perm b2c,fr
# Launch dev mode for permutation 'int' in 'it' (explicitly)
npm run dev --perm int,it
Special case : the check and build task will accept an --all-perms flag to check/build all the permutations :
# Launch check for all permutations
npm run check --all-perms
# Launch build for all permutations
npm run build --all-perms
Permutations files
All files of the project will accept a permutation rules in order to choose the more specific file.
This could be represented by a mark as an extension suffix like .(permutations), juste before the real file extension.
For example for the logical file mycode.js, we can have :
mycode.(b2c,en).jswhich represents the file for the 'b2c,en' permutation.mycode.(b2c).jswhich represents the file for any of the 'b2c' permutations.mycode.(en).jswhich represents the file for any of the 'en' permutations.mycode.jswhich represents the default file, when any other match.
By using unique permutation name and some naming convention, we can even make more precise targeting.
Ex:
mycode.(b2c,b2b).jsrepresents all the permutations with target 'b2c' or 'b2b'.mycode.(b2c,b2b,en).jsrepresents all the permutations with target 'b2c' or 'b2b', AND the lang 'en'.mycode.(-int).jsrepresents all the permutations where target is NOT 'int'
Import file
The permutation should work when we import a file, for example :
<script>
import Header from './Header.svelte';
import Footer from './Footer.svelte';
import messages from './messages.js';
import './styles.css';
</script>
<Header />
<slot/>
<Footer />
The importeds files will depends from the current permutation and the permutations rules of the file's variants.
For example, if we have theses files :
Header.(int).svelte
Header.(b2b).svelte
Header.(b2c).svelte
Footer.(b2c,fr).svelte
Footer.(b2c).svelte
Footer.(fr).svelte
Footer.(it).svelte
Footer.svelte
messages.(en).js
messages.(fr).js
messages.(de).js
messages.(it).js
styles.(b2c).css
styles.(b2b).css
styles.(intra).css
For permutation 'b2c,en', it will use Header.(b2c).svelte, Footer.(b2c).svelte, messages.(en).js and styles.(b2c).css
For permutation 'b2c,fr', it will use Header.(b2c).svelte, Footer.(b2c,fr).svelte, messages.(fr).js and styles.(b2c).css
For permutation 'b2b,en', it will use Header.(b2b).svelte, Footer.svelte, messages.(en).js and styles.(b2b).css
For permutation 'b2b,fr', it will use Header.(b2b).svelte, Footer.(fr).svelte, messages.(fr).js and styles.(b2b).css
...
The compiler should produce an error if the permutation mark is used on import :
// ERROR : invalid import with a permutation mark
// Should use 'Header.svelte' instead
import Header from 'Header.(b2c).svelte';
Static file
The same rule should apply to the static file.
For example in order to have distinct favicons :
favicon.(b2b).png
favicon.(b2c).png
favicon.(int).png
When serving/building the apps, the file will be renamed with his base name.
Routing
The svelte routing files (+page, +layout, +server) should follow the same rule, with one exception : a missing permutation will not throw an error, but simply consider that the file does not exist.
Example, with this with this route tree :
routes/
stuff/
+page.svelte
news/
+page.svelte
+page.(int).svelte
contact/
+page.(b2c).svelte
+page.(b2b).svelte
about/
+page.(b2c,fr).svelte
+page.(b2c,b2b).svelte
+layout.(b2c).svelte
+layout.(b2b).svelte
+layout.(int).svelte
- The 'stuff' page will be present on all permutations.
- The 'news' page will be present on all permutations, but the 'int' permutations will use a specific template.
- The 'contact' page will only be present on 'b2c' and 'b2b' permutations, with a specific template for each target.
- The 'about' page only be present on 'b2c' and 'b2b' permutations, with a specific template for the 'b2c,fr' permutation.
- Each target ('b2c', 'b2b' and 'int') will use a specific layout template.
Permutations directories
Another solution will be to use a permutation directory for src and static.
For example :
src/ # Will contains code shared by all permutations.
src.(b2c)/ # Will contains code for 'b2c' permutation.
static/ # Will contains static file for all permutations.
static.(b2c)/ # Will contains static file for 'b2c' permutation.
Perhaps we can event use both, so :
import Component from '$lib/Component.svelte';
For the b2c,en permutation, it will return the first file found in this order :
src.(b2c)/lib/Component.(en).svelteorsrc/lib/Component.(b2c,en).sveltesrc.(b2c)/lib/Component.svelteorsrc/lib/Component.(b2c).sveltesrc.(en)/lib/Component.svelteorsrc/lib/Component.(en).sveltesrc/lib/Component.svelte
By generating an error in case of duplicate file at the same level.
Server side processing
The handle() hook in hooks.server.js|ts will receive a new property 'permutation'.
This property will be a object representing the current permutation configuration.
For example, for the b2c,en permutation, this will match an object of the following form :
{
target: 'b2c',
lang: 'en'
}
Build mode
The build mode will define the way that Sveltekit will build the apps.
I see two main build mode.
build: 'distinct'
/// svelte.config.js
permutations: {
keys: {
target: ['b2c', 'b2b', 'int'],
lang: ['en', 'fr', 'de', 'it' ]
},
build: 'distinct'
},
In 'distinct' build mode, Sveltekit wil produce a distinct build for each permutation.
This is the default behavior described since the beginning of this document.
It should be compatible with all current adapters.
We just need to specify the build we want to dev/build on the npm run commands.
build: 'bundle'
/// svelte.config.js
permutations: {
keys: {
target: ['b2c', 'b2b', 'int'],
lang: ['en', 'fr', 'de', 'it' ]
},
build: 'bundle'
},
In ‘bundle’ build mode, Sveltekit wil produce a common build for all the permutations.
This build will include all the permutation code, and will generate an app.js for each permutation, with it's own dictionary.
The server will serve the correct client code based on the request.
Obviously this mode will be incompatible with some adapters without server-side code (like adapter-static)
In order to do that, it will require a mapping property that define all the mapping between the URL and the permutations.
For example :
/// svelte.config.js
permutations: {
keys: {
target: ['b2c', 'b2b', 'int'],
lang: ['en', 'fr', 'de', 'it' ]
},
build: 'bundle',
mapping: {
'www.my-site.com': 'b2c,en',
'www.my-site.fr': 'b2c,fr',
'www.my-site.de': 'b2c,de',
'www.my-site.it': 'b2c,it',
'www.my-site.pro/en': 'b2b,en',
'www.my-site.pro/fr': 'b2b,fr',
'www.my-site.pro/de': 'b2b,de',
'www.my-site.pro/it': 'b2b,it',
'admin.my-site.com/en': 'int,en',
'admin.my-site.pro/fr': 'int,fr',
'admin.my-site.pro/de': 'int,de',
'admin.my-site.pro/it': 'int,it',
}
},
Here www.my-site.com will serve the 'b2c,en' permutation, www.my-site.fr will serve 'b2c,fr', and so on...
We can also imagine a mapping_dev allowing to use different domain for dev mode.
An alternative/complement would be to use a server hook for that, allowing us to handle more special case :
/// hooks.server.ts
export function get_mapping(url: URL) {
switch (url.hostname) {
case 'www.my-site.com': return 'b2c,en';
case 'www.my-site.fr': return 'b2c,fr';
case 'www.my-site.de': return 'b2c,de';
case 'www.my-site.it': return 'b2c,it';
...
}
return null;
}
In case of null/incorrect permutation name, the server will return an error.
And in this case we can use the handle() to redirect to the best domain...
build: 'group'
/// svelte.config.js
permutations: {
keys: {
target: ['b2c', 'b2b', 'int'],
lang: ['en', 'fr', 'de', 'it' ]
},
build: 'group',
group_keys: ['target']
},
The ‘group’ build mode is an intermediate version.
Sveltekit will generate distinct build based on the specified group_keys, which will bundle the other permutation keys.
In this example, using group_keys: ['target'] will generation 3 main permutations :
b2cthat bundleb2c,en,b2c,fr,b2c.deandb2c.itb2bthat bundleb2b,en,b2b,fr,b2b.deandb2b.itintthat bundleint,en,int,fr,int.deandint.it
Alternatives considered
No response
Importance
nice to have
Additional Information
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the proposed svelte.config.js permutations settings and the interactions with src, static, routing files, hooks.server.js|ts, and the npm dev/check/build commands. This proposal spans file resolution, static assets, server mapping, and distinct, bundle, and group builds; work is done only after those behaviors and their scope are defined and agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- build-system, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 18/100