Skip to content

Commit da064e6

Browse files
committed
feat: separate all locales into separate files for better tree shaking
- previous code was using dynamic imports for every locales but that approach wasn't tree shakable, this new approach is requiring the user to import themselve the locale(s) that they want and is now entirely tree shakable
1 parent c2bda04 commit da064e6

23 files changed

+374
-216
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
}
4141
],
4242
"node/no-extraneous-require": "off",
43+
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
4344
"node/no-unpublished-require": "off",
4445
"node/no-unsupported-features/es-syntax": "off",
4546
"prefer-destructuring": "off",

demo/src/examples/example09.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { MultipleSelectInstance, multipleSelect } from 'multiple-select-vanilla';
22

3+
// 1. load every locale individually
4+
// import 'multiple-select-vanilla/dist/locales/multiple-select-fr-FR';
5+
// import 'multiple-select-vanilla/dist/locales/multiple-select-es-ES';
6+
7+
// 2. or load all locales at once
8+
import 'multiple-select-vanilla/dist/multiple-select-all-locales';
9+
310
export default class Example {
411
ms1?: MultipleSelectInstance;
512
ms2?: MultipleSelectInstance;
@@ -11,7 +18,7 @@ export default class Example {
1118
}) as EventListener);
1219

1320
this.ms1 = multipleSelect(elm) as MultipleSelectInstance;
14-
this.ms2 = multipleSelect('#select') as MultipleSelectInstance;
21+
this.ms2 = multipleSelect('#select', { showOkButton: true }) as MultipleSelectInstance;
1522
}
1623

1724
unmount() {

lib/build-prod.mjs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { buildSync } from 'esbuild';
2+
import glob from 'glob';
3+
4+
const buildFormats = ['cjs', 'esm'];
5+
const localeFiles = glob.sync('src/locales/**/*.ts');
6+
const localeEntryPoints = [];
7+
8+
for (const format of buildFormats) {
9+
// multiple-select.js
10+
runBuild({ format, outfile: `dist/${format}/multiple-select.js` });
11+
// runBuild({ format, outfile: `dist/multiple-select.${format === 'esm' ? 'mjs' : 'cjs'}` });
12+
}
13+
14+
// build all locales
15+
for (const localeFile of localeFiles) {
16+
// eslint-disable-next-line no-unused-vars
17+
const [_, locale] = localeFile.match(/multiple-select-(.*)\.ts$/) || [];
18+
if (locale && locale.length === 5) {
19+
localeEntryPoints.push(`src/locales/multiple-select-${locale}.ts`);
20+
runBuild({
21+
entryPoints: [`src/locales/multiple-select-${locale}.ts`],
22+
format: 'iife',
23+
outfile: `dist/locales/multiple-select-${locale}.js`,
24+
});
25+
}
26+
}
27+
28+
// also merge all Locales into a single file "multiple-select-all-locales.js"
29+
runBuild({
30+
entryPoints: ['./src/locales/all-locales-index.ts'],
31+
format: 'iife',
32+
outfile: `dist/multiple-select-all-locales.js`,
33+
});
34+
35+
// finally, create a regular bundle as a standalone which will be accessible as MultipleSelect from the global window object
36+
// this file is basically a legacy alternative to import via a <script> tag
37+
runBuild({
38+
format: 'iife',
39+
globalName: 'MultipleSelect',
40+
outfile: `dist/multiple-select.js`,
41+
});
42+
43+
function runBuild(options) {
44+
const startTime = new Date().getTime();
45+
const buildOptions = {
46+
...{
47+
color: true,
48+
entryPoints: ['./src/index.ts'],
49+
bundle: true,
50+
minify: true,
51+
target: 'es2020',
52+
sourcemap: true,
53+
logLevel: 'error',
54+
},
55+
...options,
56+
};
57+
buildSync(buildOptions);
58+
const endTime = new Date().getTime();
59+
console.info(`⚡️ Built "${buildOptions.outfile}" in ${endTime - startTime}ms`);
60+
}

lib/build-watch.mjs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,37 @@ const env = process.env.NODE_ENV;
1010
// Start the compilation process
1111
runCompilation(process.env.LERNA_FILE_CHANGES.split(','));
1212

13-
function runBuild() {
13+
function runBuild(options) {
1414
const startTime = new Date().getTime();
15-
buildSync({
16-
color: true,
17-
entryPoints: ['./src/index.ts'],
18-
bundle: true,
19-
minify: env === 'production',
20-
format: 'esm',
21-
target: 'es2020',
22-
sourcemap: false,
23-
logLevel: 'error',
24-
// outfile: env === 'production' ? './dist/multiple-select.min.js' : './dist/multiple-select.js',
25-
outfile: 'dist/esm/multiple-select.js',
26-
});
15+
const buildOptions = {
16+
...{
17+
color: true,
18+
entryPoints: ['./src/index.ts'],
19+
bundle: true,
20+
minify: env === 'production',
21+
format: 'esm',
22+
target: 'es2020',
23+
sourcemap: false,
24+
logLevel: 'error',
25+
// outfile: env === 'production' ? './dist/multiple-select.min.js' : './dist/multiple-select.js',
26+
outfile: 'dist/esm/multiple-select.js',
27+
},
28+
...options,
29+
};
30+
buildSync(buildOptions);
2731
const endTime = new Date().getTime();
2832
console.info(`⚡️ Built in ${endTime - startTime}ms`);
2933
}
3034

35+
function runLocaleBuild() {
36+
// merge all Locales into a single file "multiple-select-all-locales.js"
37+
runBuild({
38+
entryPoints: ['./src/locales/all-locales-index.ts'],
39+
format: 'iife',
40+
outfile: `dist/multiple-select-all-locales.js`,
41+
});
42+
}
43+
3144
async function runCompilation(changedFiles) {
3245
let tsLogged = false;
3346
let sassLogged = false;
@@ -42,7 +55,11 @@ async function runCompilation(changedFiles) {
4255
console.log('TypeScript file changes detected');
4356
tsLogged = true;
4457
}
45-
runBuild();
58+
if (changedFile.includes('locales')) {
59+
runLocaleBuild();
60+
} else {
61+
runBuild();
62+
}
4663
exec('pnpm run build:types', () => {});
4764
} else if (extension === '.scss') {
4865
if (!sassLogged) {

lib/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@
3838
},
3939
"scripts": {
4040
"clean": "rimraf dist",
41-
"build": "cross-env NODE_ENV=production pnpm build:web && pnpm build:cjs && pnpm build:esm && pnpm build:types:prod",
41+
"build": "pnpm build:all && pnpm build:types:prod",
4242
"postbuild": "run-s sass:build sass:copy --npm-path pnpm",
43-
"dev:init": "pnpm sass:build && pnpm sass:copy && pnpm build:types && pnpm build:esm",
43+
"dev:init": "pnpm sass:build && pnpm build:locales && pnpm sass:copy && pnpm build:types && pnpm build:esm",
44+
"build:all": "node build-prod.mjs && pnpm build:types:prod",
4445
"build:watch": "cross-env NODE_ENV='development' node build-watch.mjs",
45-
"build:web": "esbuild src/index.ts --bundle --minify --format=iife --target=es2018 --global-name=MultipleSelect --outfile=dist/multiple-select.min.js",
46+
"build:locales": "esbuild src/locales/all-locales-index.ts --bundle --minify --format=iife --target=es2018 --sourcemap --outfile=dist/multiple-select-all-locales.js",
47+
"build:web": "esbuild src/index.ts --bundle --minify --format=iife --target=es2018 --global-name=MultipleSelect --sourcemap --outfile=dist/multiple-select.min.js",
4648
"build:cjs": "esbuild src/index.ts --bundle --minify --format=cjs --target=es2018 --outfile=dist/cjs/multiple-select.js",
4749
"build:esm": "esbuild src/index.ts --bundle --minify --format=esm --target=es2018 --outfile=dist/esm/multiple-select.js",
4850
"build:types": "tsc --emitDeclarationOnly --incremental --declarationMap false --outDir dist/types",
@@ -59,6 +61,7 @@
5961
"cssnano": "^5.1.15",
6062
"esbuild": "^0.17.9",
6163
"fs-extra": "^11.1.0",
64+
"glob": "^8.1.0",
6265
"npm-run-all2": "^6.0.4",
6366
"postcss": "^8.4.21",
6467
"postcss-cli": "^10.1.0",

lib/src/MultipleSelectInstance.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from './utils/domUtils';
1616
import { BindingEventService, VirtualScroll } from './services';
1717
import { MultipleSelectOption } from './interfaces/multipleSelectOption.interface';
18-
import { OptGroupRowData, OptionRowData } from './interfaces';
18+
import { MultipleSelectLocales, OptGroupRowData, OptionRowData } from './interfaces';
1919

2020
export class MultipleSelectInstance {
2121
protected _bindEventService: BindingEventService;
@@ -48,6 +48,7 @@ export class MultipleSelectInstance {
4848
protected updateDataStart?: number;
4949
protected updateDataEnd?: number;
5050
protected virtualScroll?: VirtualScroll | null;
51+
locales: MultipleSelectLocales = {};
5152

5253
constructor(
5354
protected elm: HTMLSelectElement,
@@ -105,8 +106,7 @@ export class MultipleSelectInstance {
105106

106107
protected async initLocale() {
107108
if (this.options.locale) {
108-
const importedLocale = await import(`./locales/multiple-select-${this.options.locale}.js`);
109-
const locales = importedLocale?.default ?? importedLocale;
109+
const locales = window.multipleSelect.locales;
110110
const parts = this.options.locale.split(/-|_/);
111111

112112
parts[0] = parts[0].toLowerCase();
@@ -120,6 +120,10 @@ export class MultipleSelectInstance {
120120
Object.assign(this.options, locales[parts.join('-')]);
121121
} else if (locales[parts[0]]) {
122122
Object.assign(this.options, locales[parts[0]]);
123+
} else {
124+
throw new Error(
125+
`[multiple-select-vanilla] invalid locales "${this.options.locale}", make sure to import it before using it`
126+
);
123127
}
124128
}
125129
}

lib/src/constants.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { MultipleSelectOption } from './interfaces';
2+
import English from './locales/multiple-select-en-US';
23

34
const BLOCK_ROWS = 50;
45
const CLUSTER_BLOCKS = 4;
@@ -72,14 +73,6 @@ const DEFAULTS: Partial<MultipleSelectOption> = {
7273
onDestroyed: () => false,
7374
};
7475

75-
const EN = {
76-
formatOkButton: () => 'OK',
77-
formatSelectAll: () => '[Select all]',
78-
formatAllSelected: () => 'All selected',
79-
formatCountSelected: (count: number, total: number) => `${count} of ${total} selected`,
80-
formatNoMatchesFound: () => 'No matches found',
81-
};
82-
8376
const METHODS = [
8477
'init',
8578
'getOptions',
@@ -101,17 +94,13 @@ const METHODS = [
10194
'destroy',
10295
];
10396

104-
Object.assign(DEFAULTS, EN);
97+
Object.assign(DEFAULTS, English!['en-US']); // load English as default locale
10598

10699
const Constants = {
107100
BLOCK_ROWS,
108101
CLUSTER_BLOCKS,
109102
DEFAULTS,
110103
METHODS,
111-
LOCALES: {
112-
en: EN,
113-
'en-US': EN,
114-
},
115104
};
116105

117106
export default Constants;

lib/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export * from './locales';
21
export * from './services';
32
export * from './constants';
43
export * from './interfaces';

lib/src/interfaces/interfaces.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,24 @@ export interface VirtualScrollOption {
2222
callback: () => void;
2323
sanitizer?: (dirtyHtml: string) => string;
2424
}
25+
26+
export interface MultipleSelectLocale {
27+
/** Customize the formatted text "All Selected" when using custom locale. */
28+
formatAllSelected(): string;
29+
30+
/** Customize the formatted text "x of y selected" when using custom locale. */
31+
formatCountSelected(selectedCount: number, totalCount: number): string;
32+
33+
/** For the "No Matches Found" text when nothing is found while filtering the dropdown */
34+
formatNoMatchesFound(): string;
35+
36+
/** Customize the formatted text "OK" showing at the bottom of the drop. */
37+
formatOkButton(): string;
38+
39+
/** For the "Select All" checkbox text */
40+
formatSelectAll(): string;
41+
}
42+
43+
export interface MultipleSelectLocales {
44+
[localeKey: string]: MultipleSelectLocale;
45+
}

lib/src/interfaces/multipleSelectOption.interface.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { OptGroupRowData, OptionRowData } from './interfaces';
1+
import { MultipleSelectLocale, OptGroupRowData, OptionRowData } from './interfaces';
22

3-
export interface MultipleSelectOption {
3+
export interface MultipleSelectOption extends MultipleSelectLocale {
44
/** @deprecated @alias `displayTitle` Add a title. By default this option is set to false. */
55
addTitle?: boolean;
66

@@ -134,21 +134,6 @@ export interface MultipleSelectOption {
134134
/** Customize the filter method, for example we use startWith */
135135
customFilter: (normalizedText: string, normalizedOriginalText: string, text: string, originalText: string) => boolean;
136136

137-
/** Customize the formatted text "All Selected" when using custom locale. */
138-
formatAllSelected: () => string;
139-
140-
/** Customize the formatted text "x of y selected" when using custom locale. */
141-
formatCountSelected: (selectedCount: number, totalCount: number) => string;
142-
143-
/** For the "No Matches Found" text when nothing is found while filtering the dropdown */
144-
formatNoMatchesFound: () => string;
145-
146-
/** Customize the formatted text "OK" showing at the bottom of the drop. */
147-
formatOkButton: () => string;
148-
149-
/** For the "Select All" checkbox text */
150-
formatSelectAll: () => string;
151-
152137
/** The item styler function, return style string to custom the item style such as background: red. The function take one parameter: value. */
153138
styler: (value: OptionRowData | OptGroupRowData) => string | boolean | null;
154139

0 commit comments

Comments
 (0)