file-variants
v1.2.0
Published
Create multiple file variants, build with one (and replace within if needed), and deploy. Solves the problem of using multiple somewhat similar files in a sort of multitenant system. Highly configurable and flexible pseudo multitenant file builder.
Downloads
4
Maintainers
Readme
Sometimes you need to have multiple slightly different variants of the same file, say a different logo for each of your clients, but you want to keep your codebase to one single instance with smaller parts being switched out depending on client, platform, behaviour etc. (a sort of pseudo multitenancy). file-variants is an attempt to solve one part of this problem: using multiple file variants, but only building with one of them at a time.
Installation
npm install --save-dev file-variants
Running
package.json
Add the following to your package.json file:
{
"scripts": {
"fv-build": "file-variants build"
}
}
npm / npx
Now run npm run fv-build
(or npx file-variants build
)
Contents
Usage
build
build [variant]
common options:
path=path # specify path to search in (relative from cwd)
config=path # specify path to global config (relative from cwd)
include=[,input] # inputs to include
exclude=[,input] # inputs to exclude
override=input,variant # override the variant for a specific input (repeatable)
replace=input,keyword,replacement # replacements for a specific input (repeatable)
global-replace=keyword,replacement # replacements for all inputs (repeatable)
verbose # debug info
Naming convention
Because naming is difficult, please study the file structure below meant to represent the build of an imaginary logo image in order to understand what we've named the different parts of file-variants.
.
+-- _src
| +-- _Logo # input (directory)
| +-- fvi.config.json # config
| +-- foo.png # file variant --\
| +-- bar.png # file variant ---|--> file variant*s*
| +-- foobar.png # file variant --/
| +-- Logo.png # output
| ...
Parts of a build
Here each part mentioned in Naming convention will be explained.
Input
- type: directory
- purpose: to contain a file-variant-input (fvi) config and all the file variants of this input.
Config
- type: .json
- purpose: to provide values for file-variants.
NOTE: Configs must always be named "fvi.config.json".
type Variant = string;
type Encoding = 'utf8' | 'utf-8' | 'ascii' | 'base64' | 'binary' | 'hex' | 'latin1' | 'ucs-2' | 'ucs2' | 'utf16le';
interface Config {
default: Variant;
name?: string;
outputName?: string;
encoding?: Encoding;
outPath?: string;
fallbacks?: {
[variant: Variant]: Variant;
};
useGlobalReplacements?: boolean | string[];
marking?: boolean | string;
exclude?: string;
}
default
(required): the default variant to use as fallback if all else fails.name
: name of input. If left empty the input directory's name will be used instead.outputName
: name of output (excluding extension). If left empty,name
will be used instead.encoding
: common encoding of all file variants. If left empty,utf8
will be used. However,encoding
only matters if replacements are being used on the output. If replacements are provided and are to be used,encoding
must be any of the following values:ascii
,utf8
,utf-8
, orlatin1
.outPath
: relative (from input's path) path to place output at.- Unprovided: output will be placed on the same level as input.
- Empty: output will be placed inside input.
- Otherwise: relative from path of input.
fallbacks
: a map of variant to variant fallbacks. If a variant isn't detected inside input, but is detected insidefallbacks
as a key, the corresponding key's value will be used as the variant instead. This will be done recursively until a valid file variant is found, or until no fallbacks remain, in which casedefault
will be used as a variant instead.useGlobalReplacements
: control what keywords should be replaced by global-replace(s) (option).- Unprovided/false: output will not receive any replacements from global-replace(s).
- True: output will use replacements from all found global replaces.
- Array: output will only use replacements found both in global-replace(s) and provided array.
marking
: mark the output to ignore changes more easily.- Unprovided/false: output will not be marked.
- True: will append ".fvo" (not extension) to outputName.
- String: will append given string to outputName OR replace keyword "{marking}" inside outputName.
exclude
: which file variants to exclude (regex). Matches entire filename (including variant).
File variants
- type: .* (output will use the same extension as selected file variant)
- purpose: file variant.
Output
- type: .* (same as selected file variant)
- purpose: to be used in your project.
Global config
If you need many cli arguments when building, using a global config may be easier. You can specify which global config to use with the config=[path]
argument passed to build
.
Global configs must always be .json-files.
interface GlobalConfig {
values?: {
variant?: string;
};
options?: string[];
overrides?: {
marking?: boolean | string;
};
}
Examples
The files for each example can be found under examples/.
Example 1
You have 3 clients who use your product. You want to keep a shared codebase, where the only difference between these 3 clients is their logo.
1. Before build
.
+-- _src
| +-- _Logo
| +-- fvi.config.json
| +-- foo.png
| +-- bar.png
| +-- foobar.png
| ...
2. Build
npm run fv-build foobar
3. After build
Logo.png is now a direct copy of foobar.png. You can now always import Logo.png instead of checking for the current client or doing lazy imports (which require you to build with all logos included).
.
+-- _src
| +-- _Logo
| +-- fvi.config.json
| +-- foo.png
| +-- bar.png
| +-- foobar.png
| +-- Logo.png # copy of src/Logo/foobar.png
| ...
Example 2
- You use 3 different color.ini [variant-]files.
- You build your project with variant foo.
- You only have colors for a, b, and c.
- Variant foo should not use a (default), but b as its fallback.
1. Before build
.
+-- _src
| +-- _colors
| +-- fvi.config.json
| +-- colors_a.ini
| +-- colors_b.ini
| +-- colors_c.ini
| ...
fvi.config.json
{
"default": "colors_a",
"fallbacks": {
"foo": "colors_b"
},
"marking": true
}
colors_b.ini
[main]
primary=rgba(255, 0, 255, ALPHA_VALUE)
secondary=rgba(0, 255, 0, ALPHA_VALUE)
2. Build
npm run fv-build foo replace=colors,ALPHA_VALUE,0.5
3. After build
.
+-- _src
| +-- _colors
| +-- fvi.config.json
| +-- colors_a.ini
| +-- colors_b.ini
| +-- colors_c.ini
| +-- colors.fvo.ini # copy of src/colors/colors_b.ini
| ...
colors.fvo.ini
[main]
primary=rgba(255, 0, 255, 0.5)
secondary=rgba(0, 255, 0, 0.5)
.gitignore
# file-variants outputs
*.fvo*
Example 3
- You have 3 different splash image resolutions (64x64, 128x128, and 256x256).
- Each variant has 3 resolutions (mentioned above).
- You build your project with variant red.
- You want the outputs to be named splash_RESOLUTION.png.
1. Before build
.
+-- _src
| +-- _splash
| +-- fvi.config.json
| +-- red.x64.png
| +-- red.x128.png
| +-- red.x256.png
| +-- green.x64.png
| +-- green.x128.png
| +-- green.x256.png
| +-- blue.x64.png
| +-- blue.x128.png
| +-- blue.x256.png
| ...
fvi.config.json
{
"default": "red",
"//": "{part0} (pattern {part}\\d*} with only one (1) backslash) means the first part of the",
"//": "filename after variant (ie. red/green/blue in this case).",
"outputName": "{name}_{part0}"
}
2. Build
npm run fv-build
3. After build
.
+-- _src
| +-- _splash
| +-- fvi.config.json
| +-- red.x64.png
| +-- red.x128.png
| +-- red.x256.png
| +-- green.x64.png
| +-- green.x128.png
| +-- green.x256.png
| +-- blue.x64.png
| +-- blue.x128.png
| +-- blue.x256.png
| +-- splash_x64.png
| +-- splash_x128.png
| +-- splash_x256.png
| ...
.gitignore
# file-variants outputs
splash*.png
Example 4
- You have different configs and a README for each.
- You don't want the READMEs to also be outputted.
1. Before build
.
+-- _src
| +-- _config
| +-- fvi.config.json
| +-- A.ini
| +-- A.README.md
| +-- B.ini
| +-- B.README.md
| +-- C.ini
| +-- C.README.md
| ...
fvi.config.json
{
"default": "A",
"exclude": "README"
}
2. Build
npm run fv-build
3. After build
.
+-- _src
| +-- _config
| +-- fvi.config.json
| +-- A.ini
| +-- A.README.md
| +-- B.ini
| +-- B.README.md
| +-- C.ini
| +-- C.README.md
| +-- config.ini # No README as output!
| ...
.gitignore
# file-variants outputs
config.ini
License
MIT. Copyright (c) 2021 Emil Engelin.