@tramvai/cli 2.98.2 → 2.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/start-prod/providers/application.js +2 -3
- package/lib/api/start-prod/providers/application.js.map +1 -1
- package/lib/builder/webpack/tokens.d.ts +3 -0
- package/lib/commands/analyze/command.js +7 -1
- package/lib/commands/analyze/command.js.map +1 -1
- package/lib/commands/build/command.js +2 -0
- package/lib/commands/build/command.js.map +1 -1
- package/lib/commands/start/command.js +2 -0
- package/lib/commands/start/command.js.map +1 -1
- package/lib/commands/start-prod/command.js +7 -1
- package/lib/commands/start-prod/command.js.map +1 -1
- package/lib/commands/static/application.js +1 -1
- package/lib/commands/static/application.js.map +1 -1
- package/lib/config/configManager.d.ts +1 -0
- package/lib/config/configManager.js +23 -1
- package/lib/config/configManager.js.map +1 -1
- package/lib/di/tokens/config.d.ts +1 -0
- package/lib/library/webpack/application/client/common.js +2 -0
- package/lib/library/webpack/application/client/common.js.map +1 -1
- package/lib/library/webpack/application/server/common.js +2 -0
- package/lib/library/webpack/application/server/common.js.map +1 -1
- package/lib/library/webpack/blocks/pwa/client.d.ts +4 -0
- package/lib/library/webpack/blocks/pwa/client.js +62 -0
- package/lib/library/webpack/blocks/pwa/client.js.map +1 -0
- package/lib/library/webpack/blocks/pwa/server.d.ts +4 -0
- package/lib/library/webpack/blocks/pwa/server.js +9 -0
- package/lib/library/webpack/blocks/pwa/server.js.map +1 -0
- package/lib/library/webpack/blocks/pwa/shared.d.ts +4 -0
- package/lib/library/webpack/blocks/pwa/shared.js +18 -0
- package/lib/library/webpack/blocks/pwa/shared.js.map +1 -0
- package/lib/library/webpack/plugins/PwaIconsPlugin.d.ts +9 -0
- package/lib/library/webpack/plugins/PwaIconsPlugin.js +78 -0
- package/lib/library/webpack/plugins/PwaIconsPlugin.js.map +1 -0
- package/lib/library/webpack/plugins/WebManifestPlugin.d.ts +8 -0
- package/lib/library/webpack/plugins/WebManifestPlugin.js +28 -0
- package/lib/library/webpack/plugins/WebManifestPlugin.js.map +1 -0
- package/lib/schema/autogeneratedSchema.json +183 -0
- package/lib/typings/configEntry/application.d.ts +54 -0
- package/lib/typings/public.d.ts +1 -0
- package/lib/typings/public.js +2 -0
- package/lib/typings/public.js.map +1 -1
- package/lib/typings/pwa/index.d.ts +59 -0
- package/lib/typings/pwa/index.js +3 -0
- package/lib/typings/pwa/index.js.map +1 -0
- package/lib/validators/commands/checkPwaDependencies.d.ts +2 -0
- package/lib/validators/commands/checkPwaDependencies.js +18 -0
- package/lib/validators/commands/checkPwaDependencies.js.map +1 -0
- package/package.json +15 -11
- package/schema.json +183 -0
- package/src/api/start-prod/providers/application.ts +3 -4
- package/src/commands/analyze/command.ts +7 -1
- package/src/commands/build/command.ts +2 -0
- package/src/commands/start/command.ts +2 -0
- package/src/commands/start-prod/command.ts +7 -1
- package/src/commands/static/application.ts +1 -4
- package/src/config/configManager.ts +37 -0
- package/src/library/webpack/application/client/common.ts +2 -0
- package/src/library/webpack/application/server/common.ts +2 -0
- package/src/library/webpack/blocks/pwa/client.ts +83 -0
- package/src/library/webpack/blocks/pwa/server.ts +9 -0
- package/src/library/webpack/blocks/pwa/shared.ts +26 -0
- package/src/library/webpack/plugins/PwaIconsPlugin.ts +87 -0
- package/src/library/webpack/plugins/WebManifestPlugin.ts +32 -0
- package/src/models/config.spec.ts +54 -0
- package/src/schema/autogeneratedSchema.json +183 -0
- package/src/schema/tramvai.spec.ts +27 -0
- package/src/typings/configEntry/application.ts +55 -0
- package/src/typings/public.ts +2 -0
- package/src/typings/pwa/index.ts +64 -0
- package/src/validators/commands/checkPwaDependencies.ts +17 -0
- package/lib/utils/webpackBuild.d.ts +0 -5
- package/lib/utils/webpackBuild.js +0 -32
- package/lib/utils/webpackBuild.js.map +0 -1
- package/src/utils/webpackBuild.ts +0 -38
package/schema.json
CHANGED
|
@@ -546,6 +546,189 @@
|
|
|
546
546
|
],
|
|
547
547
|
"type": "string"
|
|
548
548
|
},
|
|
549
|
+
"pwa": {
|
|
550
|
+
"title": "PWA configuration (works with `TramvaiPwaModule` from `@tramvai/module-progressive-web-app` library)",
|
|
551
|
+
"default": {},
|
|
552
|
+
"type": "object",
|
|
553
|
+
"properties": {
|
|
554
|
+
"sw": {
|
|
555
|
+
"title": "Service-Worker configuration",
|
|
556
|
+
"default": {},
|
|
557
|
+
"type": "object",
|
|
558
|
+
"properties": {
|
|
559
|
+
"src": {
|
|
560
|
+
"title": "Path to sw.ts file (relative to \"root\" directory)",
|
|
561
|
+
"default": "sw.ts",
|
|
562
|
+
"type": "string"
|
|
563
|
+
},
|
|
564
|
+
"dest": {
|
|
565
|
+
"title": "Name of generated SW file (will be placed in \"output.client\" directory)",
|
|
566
|
+
"default": "sw.js",
|
|
567
|
+
"type": "string"
|
|
568
|
+
},
|
|
569
|
+
"scope": {
|
|
570
|
+
"title": "Scope of SW (see https://developers.google.com/web/ilt/pwa/introduction-to-service-worker#registration_and_scope)",
|
|
571
|
+
"default": "/",
|
|
572
|
+
"type": "string"
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
"additionalProperties": false
|
|
576
|
+
},
|
|
577
|
+
"workbox": {
|
|
578
|
+
"title": "Workbox configuration",
|
|
579
|
+
"default": {},
|
|
580
|
+
"type": "object",
|
|
581
|
+
"properties": {
|
|
582
|
+
"enabled": {
|
|
583
|
+
"cli_overridable": "",
|
|
584
|
+
"title": "Connect `InjectManifest` from `workbox-webpack-plugin` library",
|
|
585
|
+
"default": false,
|
|
586
|
+
"anyOf": [
|
|
587
|
+
{
|
|
588
|
+
"type": "object",
|
|
589
|
+
"properties": {
|
|
590
|
+
"development": {
|
|
591
|
+
"type": "boolean"
|
|
592
|
+
},
|
|
593
|
+
"production": {
|
|
594
|
+
"type": "boolean"
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
"additionalProperties": false
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
"type": "boolean"
|
|
601
|
+
}
|
|
602
|
+
]
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
"additionalProperties": false
|
|
606
|
+
},
|
|
607
|
+
"webmanifest": {
|
|
608
|
+
"title": "WebManifest content (manifest.json or webmanifest will be generated based on this options)",
|
|
609
|
+
"default": {},
|
|
610
|
+
"type": "object",
|
|
611
|
+
"properties": {
|
|
612
|
+
"enabled": {
|
|
613
|
+
"title": "Create webmanifest file",
|
|
614
|
+
"default": false,
|
|
615
|
+
"type": "boolean"
|
|
616
|
+
},
|
|
617
|
+
"dest": {
|
|
618
|
+
"title": "Name of generated manifest file (will be placed in \"output.client\" directory). You can use `[hash]` placeholder for manifest cache busting",
|
|
619
|
+
"default": "/manifest.[hash].json",
|
|
620
|
+
"type": "string"
|
|
621
|
+
},
|
|
622
|
+
"scope": {
|
|
623
|
+
"title": "prefer to use \"pwa.sw.scope\" instead, this field will be generated automatically",
|
|
624
|
+
"type": "string"
|
|
625
|
+
},
|
|
626
|
+
"name": {
|
|
627
|
+
"type": "string"
|
|
628
|
+
},
|
|
629
|
+
"short_name": {
|
|
630
|
+
"type": "string"
|
|
631
|
+
},
|
|
632
|
+
"description": {
|
|
633
|
+
"type": "string"
|
|
634
|
+
},
|
|
635
|
+
"start_url": {
|
|
636
|
+
"type": "string"
|
|
637
|
+
},
|
|
638
|
+
"display": {
|
|
639
|
+
"type": "string"
|
|
640
|
+
},
|
|
641
|
+
"theme_color": {
|
|
642
|
+
"title": "prefer to use \"pwa.meta.themeColor\" instead, this field will be generated automatically",
|
|
643
|
+
"type": "string"
|
|
644
|
+
},
|
|
645
|
+
"background_color": {
|
|
646
|
+
"type": "string"
|
|
647
|
+
},
|
|
648
|
+
"icons": {
|
|
649
|
+
"title": "prefer to use \"pwa.icon\" instead, this field will be generated automatically",
|
|
650
|
+
"type": "array",
|
|
651
|
+
"items": {
|
|
652
|
+
"type": "object",
|
|
653
|
+
"properties": {
|
|
654
|
+
"src": {
|
|
655
|
+
"type": "string"
|
|
656
|
+
},
|
|
657
|
+
"sizes": {
|
|
658
|
+
"type": "string"
|
|
659
|
+
},
|
|
660
|
+
"type": {
|
|
661
|
+
"type": "string"
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
"additionalProperties": false
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
"additionalProperties": false
|
|
669
|
+
},
|
|
670
|
+
"icon": {
|
|
671
|
+
"title": "PWA icons options",
|
|
672
|
+
"default": {},
|
|
673
|
+
"type": "object",
|
|
674
|
+
"properties": {
|
|
675
|
+
"src": {
|
|
676
|
+
"title": "Path to icon file (relative to \"root\" directory)",
|
|
677
|
+
"type": "string"
|
|
678
|
+
},
|
|
679
|
+
"dest": {
|
|
680
|
+
"title": "Folder for generated icons (will be placed in \"output.client\" directory)",
|
|
681
|
+
"default": "pwa-icons",
|
|
682
|
+
"type": "string"
|
|
683
|
+
},
|
|
684
|
+
"sizes": {
|
|
685
|
+
"title": "Icon sizes",
|
|
686
|
+
"default": [
|
|
687
|
+
36,
|
|
688
|
+
48,
|
|
689
|
+
72,
|
|
690
|
+
96,
|
|
691
|
+
144,
|
|
692
|
+
192,
|
|
693
|
+
512
|
|
694
|
+
],
|
|
695
|
+
"type": "array",
|
|
696
|
+
"items": {
|
|
697
|
+
"type": "number"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
"additionalProperties": false
|
|
702
|
+
},
|
|
703
|
+
"meta": {
|
|
704
|
+
"title": "PWA meta options",
|
|
705
|
+
"default": {},
|
|
706
|
+
"type": "object",
|
|
707
|
+
"properties": {
|
|
708
|
+
"viewport": {
|
|
709
|
+
"type": "string"
|
|
710
|
+
},
|
|
711
|
+
"themeColor": {
|
|
712
|
+
"type": "string"
|
|
713
|
+
},
|
|
714
|
+
"mobileApp": {
|
|
715
|
+
"type": "string"
|
|
716
|
+
},
|
|
717
|
+
"mobileAppIOS": {
|
|
718
|
+
"type": "string"
|
|
719
|
+
},
|
|
720
|
+
"appleTitle": {
|
|
721
|
+
"type": "string"
|
|
722
|
+
},
|
|
723
|
+
"appleStatusBarStyle": {
|
|
724
|
+
"type": "string"
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
"additionalProperties": false
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
"additionalProperties": false
|
|
731
|
+
},
|
|
549
732
|
"webpack": {
|
|
550
733
|
"title": "experiments configuration for [webpack](https://webpack.js.org/configuration/experiments/)",
|
|
551
734
|
"default": {},
|
|
@@ -46,7 +46,7 @@ export const applicationsProviders: readonly Provider[] = [
|
|
|
46
46
|
).withSettings({
|
|
47
47
|
buildType: 'server',
|
|
48
48
|
});
|
|
49
|
-
const { debug, port,
|
|
49
|
+
const { debug, port, assetsPrefix } = serverConfigManager;
|
|
50
50
|
const root = serverConfigManager.buildPath;
|
|
51
51
|
|
|
52
52
|
return fork(path.resolve(root, 'server.js'), [], {
|
|
@@ -58,11 +58,10 @@ export const applicationsProviders: readonly Provider[] = [
|
|
|
58
58
|
...env,
|
|
59
59
|
...process.env,
|
|
60
60
|
NODE_ENV: 'production',
|
|
61
|
+
TRAMVAI_CLI_COMMAND: 'start-prod',
|
|
61
62
|
PORT: `${port}`,
|
|
62
63
|
PORT_SERVER: `${port}`,
|
|
63
|
-
ASSETS_PREFIX:
|
|
64
|
-
process.env.ASSETS_PREFIX ??
|
|
65
|
-
`http://${staticHost}:${staticPort}/${output.client.replace(/\/$/, '')}/`,
|
|
64
|
+
ASSETS_PREFIX: assetsPrefix,
|
|
66
65
|
},
|
|
67
66
|
});
|
|
68
67
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CLICommand } from '../../models/command';
|
|
2
2
|
import { checkApplication } from '../../validators/commands/checkBuild';
|
|
3
3
|
import { checkConfigExists } from '../../validators/commands/checkConfigExists';
|
|
4
|
+
import { checkPwaDependencies } from '../../validators/commands/checkPwaDependencies';
|
|
4
5
|
import { runMigrationsAndCheckVersions } from '../../validators/commands/runMigrationsAndCheckVersions';
|
|
5
6
|
|
|
6
7
|
export type Params = {
|
|
@@ -38,7 +39,12 @@ class AnalyzeCommand extends CLICommand<Params> {
|
|
|
38
39
|
|
|
39
40
|
alias = 'a';
|
|
40
41
|
|
|
41
|
-
validators = [
|
|
42
|
+
validators = [
|
|
43
|
+
checkConfigExists,
|
|
44
|
+
checkApplication,
|
|
45
|
+
runMigrationsAndCheckVersions,
|
|
46
|
+
checkPwaDependencies,
|
|
47
|
+
];
|
|
42
48
|
|
|
43
49
|
action(parameters: Params) {
|
|
44
50
|
// used require for lazy code execution
|
|
@@ -4,6 +4,7 @@ import { checkConfigExists } from '../../validators/commands/checkConfigExists';
|
|
|
4
4
|
import { checkDependencies } from '../../validators/commands/checkDependencies';
|
|
5
5
|
import { runMigrationsAndCheckVersions } from '../../validators/commands/runMigrationsAndCheckVersions';
|
|
6
6
|
import type { BuildCommand as BuildCommandType } from '../../api/build';
|
|
7
|
+
import { checkPwaDependencies } from '../../validators/commands/checkPwaDependencies';
|
|
7
8
|
|
|
8
9
|
export type Params = Parameters<BuildCommandType>[0] & {
|
|
9
10
|
target: string;
|
|
@@ -65,6 +66,7 @@ class BuildCommand extends CLICommand<Params> {
|
|
|
65
66
|
checkApplication,
|
|
66
67
|
runMigrationsAndCheckVersions,
|
|
67
68
|
checkDependencies,
|
|
69
|
+
checkPwaDependencies,
|
|
68
70
|
];
|
|
69
71
|
|
|
70
72
|
action(parameters: Params) {
|
|
@@ -4,6 +4,7 @@ import { checkConfigExists } from '../../validators/commands/checkConfigExists';
|
|
|
4
4
|
import { checkDependencies } from '../../validators/commands/checkDependencies';
|
|
5
5
|
import { runMigrationsAndCheckVersions } from '../../validators/commands/runMigrationsAndCheckVersions';
|
|
6
6
|
import type { StartCommand as StartCommandType } from '../../api/start';
|
|
7
|
+
import { checkPwaDependencies } from '../../validators/commands/checkPwaDependencies';
|
|
7
8
|
|
|
8
9
|
export type Params = Parameters<StartCommandType>[0] & {
|
|
9
10
|
target: string;
|
|
@@ -113,6 +114,7 @@ export class StartCommand extends CLICommand<Params> {
|
|
|
113
114
|
checkApplication,
|
|
114
115
|
runMigrationsAndCheckVersions,
|
|
115
116
|
checkDependencies,
|
|
117
|
+
checkPwaDependencies,
|
|
116
118
|
];
|
|
117
119
|
|
|
118
120
|
action(parameters) {
|
|
@@ -2,6 +2,7 @@ import { CLICommand } from '../../models/command';
|
|
|
2
2
|
import { checkConfigExists } from '../../validators/commands/checkConfigExists';
|
|
3
3
|
import { checkApplication } from '../../validators/commands/checkBuild';
|
|
4
4
|
import { runMigrationsAndCheckVersions } from '../../validators/commands/runMigrationsAndCheckVersions';
|
|
5
|
+
import { checkPwaDependencies } from '../../validators/commands/checkPwaDependencies';
|
|
5
6
|
|
|
6
7
|
export interface Params {
|
|
7
8
|
target: string;
|
|
@@ -82,7 +83,12 @@ export class StartProdCommand extends CLICommand<Params> {
|
|
|
82
83
|
|
|
83
84
|
alias = 'sp';
|
|
84
85
|
|
|
85
|
-
validators = [
|
|
86
|
+
validators = [
|
|
87
|
+
checkConfigExists,
|
|
88
|
+
checkApplication,
|
|
89
|
+
runMigrationsAndCheckVersions,
|
|
90
|
+
checkPwaDependencies,
|
|
91
|
+
];
|
|
86
92
|
|
|
87
93
|
action(parameters: Params) {
|
|
88
94
|
// used require for lazy code execution
|
|
@@ -72,10 +72,7 @@ export const staticApp = async (
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
const staticServer = await startStaticServer(clientConfigManager);
|
|
75
|
-
const staticAssetsPrefix =
|
|
76
|
-
/\/$/,
|
|
77
|
-
''
|
|
78
|
-
)}/`;
|
|
75
|
+
const staticAssetsPrefix = serverConfigManager.assetsPrefix;
|
|
79
76
|
|
|
80
77
|
const server = node(path.resolve(root, 'server.js'), [], {
|
|
81
78
|
cwd: root,
|
|
@@ -86,12 +86,14 @@ export type ConfigManager<
|
|
|
86
86
|
buildPath: string;
|
|
87
87
|
withSettings(settings: Settings<E>): ConfigManager<C, E>;
|
|
88
88
|
dehydrate(): [C, Settings<E>];
|
|
89
|
+
assetsPrefix?: string;
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
export const DEFAULT_PORT = 3000;
|
|
92
93
|
export const DEFAULT_STATIC_PORT = 4000;
|
|
93
94
|
export const DEFAULT_STATIC_MODULE_PORT = 4040;
|
|
94
95
|
|
|
96
|
+
// eslint-disable-next-line max-statements, complexity
|
|
95
97
|
export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E extends Env = Env>(
|
|
96
98
|
configEntry: C,
|
|
97
99
|
settings: Settings<E>
|
|
@@ -165,10 +167,45 @@ export const createConfigManager = <C extends ConfigEntry = ConfigEntry, E exten
|
|
|
165
167
|
};
|
|
166
168
|
|
|
167
169
|
if (isApplication(config)) {
|
|
170
|
+
config.assetsPrefix =
|
|
171
|
+
process.env.ASSETS_PREFIX && process.env.ASSETS_PREFIX !== 'static'
|
|
172
|
+
? process.env.ASSETS_PREFIX
|
|
173
|
+
: `http://${config.staticHost}:${config.staticPort}/${config.output.client.replace(
|
|
174
|
+
/\/$/,
|
|
175
|
+
''
|
|
176
|
+
)}/`;
|
|
168
177
|
config.buildPath = resolve(
|
|
169
178
|
rootDir,
|
|
170
179
|
buildType === 'server' ? config.output.server : config.output.client
|
|
171
180
|
);
|
|
181
|
+
|
|
182
|
+
const pwa = config.experiments?.pwa;
|
|
183
|
+
|
|
184
|
+
if (pwa.webmanifest?.enabled) {
|
|
185
|
+
pwa.webmanifest = {
|
|
186
|
+
icons: pwa.icon?.src
|
|
187
|
+
? pwa.icon.sizes.map((size) => ({
|
|
188
|
+
src: `${config.assetsPrefix}${pwa.icon.dest}/${size}x${size}.png`,
|
|
189
|
+
sizes: `${size}x${size}`,
|
|
190
|
+
type: 'image/png',
|
|
191
|
+
}))
|
|
192
|
+
: [],
|
|
193
|
+
...pwa.webmanifest,
|
|
194
|
+
scope: pwa.webmanifest.scope ?? pwa.sw?.scope,
|
|
195
|
+
name: pwa.webmanifest.name ?? config.name,
|
|
196
|
+
short_name: pwa.webmanifest.name ?? config.name,
|
|
197
|
+
theme_color: pwa.webmanifest.theme_color ?? pwa.meta.themeColor,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
if (pwa.webmanifest.dest.includes('[hash]')) {
|
|
201
|
+
const crypto = require('crypto');
|
|
202
|
+
const hashSum = crypto.createHash('sha256');
|
|
203
|
+
hashSum.update(JSON.stringify(pwa.webmanifest));
|
|
204
|
+
const currentHash = hashSum.digest('hex');
|
|
205
|
+
|
|
206
|
+
pwa.webmanifest.dest = pwa.webmanifest.dest.replace('[hash]', currentHash.substr(0, 8));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
172
209
|
} else if (isChildApp(config)) {
|
|
173
210
|
config.buildPath = resolve(rootDir, ...config.output.split('/'));
|
|
174
211
|
} else if (isModule(config)) {
|
|
@@ -19,6 +19,7 @@ import nodeClient from '../../blocks/nodeClient';
|
|
|
19
19
|
import { pagesResolve } from '../../blocks/pagesResolve';
|
|
20
20
|
import { configToEnv } from '../../blocks/configToEnv';
|
|
21
21
|
import { DEFAULT_STATS_OPTIONS, DEFAULT_STATS_FIELDS } from '../../constants/stats';
|
|
22
|
+
import { pwaBlock } from '../../blocks/pwa/client';
|
|
22
23
|
|
|
23
24
|
export default (configManager: ConfigManager<ApplicationConfigEntry>) => (config: Config) => {
|
|
24
25
|
const { polyfill, fileSystemPages } = configManager;
|
|
@@ -41,6 +42,7 @@ export default (configManager: ConfigManager<ApplicationConfigEntry>) => (config
|
|
|
41
42
|
.batch(css(configManager))
|
|
42
43
|
.batch(nodeClient(configManager))
|
|
43
44
|
.batch(postcssAssets(configManager))
|
|
45
|
+
.batch(pwaBlock(configManager))
|
|
44
46
|
.when(fileSystemPages.enabled, (cfg) => cfg.batch(pagesResolve(configManager)));
|
|
45
47
|
|
|
46
48
|
config
|
|
@@ -17,6 +17,7 @@ import { browserslistConfigResolve } from '../../blocks/browserslistConfig';
|
|
|
17
17
|
import { configToEnv } from '../../blocks/configToEnv';
|
|
18
18
|
import { commonApplication } from '../common';
|
|
19
19
|
import { extractCssPluginFactory } from '../../blocks/extractCssPlugin';
|
|
20
|
+
import { pwaBlock } from '../../blocks/pwa/server';
|
|
20
21
|
|
|
21
22
|
// eslint-disable-next-line import/no-default-export
|
|
22
23
|
export default (configManager: ConfigManager<ApplicationConfigEntry>) => (config: Config) => {
|
|
@@ -45,6 +46,7 @@ export default (configManager: ConfigManager<ApplicationConfigEntry>) => (config
|
|
|
45
46
|
})
|
|
46
47
|
)
|
|
47
48
|
.batch(css(configManager))
|
|
49
|
+
.batch(pwaBlock(configManager))
|
|
48
50
|
.when(fileSystemPages.enabled, (cfg) => cfg.batch(pagesResolve(configManager)));
|
|
49
51
|
|
|
50
52
|
config.output
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import type Config from 'webpack-chain';
|
|
3
|
+
import { InjectManifest } from 'workbox-webpack-plugin';
|
|
4
|
+
import type { ConfigManager } from '../../../../config/configManager';
|
|
5
|
+
import type { ApplicationConfigEntry } from '../../../../typings/configEntry/application';
|
|
6
|
+
import { PwaIconsPlugin } from '../../plugins/PwaIconsPlugin';
|
|
7
|
+
import { WebManifestPlugin } from '../../plugins/WebManifestPlugin';
|
|
8
|
+
import { pwaSharedBlock } from './shared';
|
|
9
|
+
|
|
10
|
+
export const pwaBlock =
|
|
11
|
+
(configManager: ConfigManager<ApplicationConfigEntry>) => (config: Config) => {
|
|
12
|
+
const {
|
|
13
|
+
experiments: { pwa },
|
|
14
|
+
rootDir,
|
|
15
|
+
root,
|
|
16
|
+
output,
|
|
17
|
+
env,
|
|
18
|
+
modern,
|
|
19
|
+
sourceMap,
|
|
20
|
+
assetsPrefix,
|
|
21
|
+
} = configManager;
|
|
22
|
+
|
|
23
|
+
config.batch(pwaSharedBlock(configManager));
|
|
24
|
+
|
|
25
|
+
// @todo check `@tramvai/module-progressive-web-app` is installed
|
|
26
|
+
|
|
27
|
+
if (pwa.workbox?.enabled) {
|
|
28
|
+
// @todo check `sw.ts` exists
|
|
29
|
+
// @todo: static HTML caching ??? full offline mode for tramvai static ???
|
|
30
|
+
const workboxOptions: InjectManifest['config'] = {
|
|
31
|
+
swSrc: path.join(rootDir, root, pwa.sw?.src),
|
|
32
|
+
swDest: path.join(rootDir, output.client, pwa.sw?.dest),
|
|
33
|
+
exclude: [/hmr\.js$/, /\.map$/, /\.hot-update\./],
|
|
34
|
+
// @todo maybe less for production?
|
|
35
|
+
maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (env === 'production') {
|
|
39
|
+
workboxOptions.modifyURLPrefix = {
|
|
40
|
+
'': assetsPrefix,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (modern) {
|
|
45
|
+
workboxOptions.swDest = workboxOptions.swDest.replace(/\.js$/, '.modern.js');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// @todo: break hmr on client when sw.ts is changed - infinity loop !!!
|
|
49
|
+
|
|
50
|
+
const workboxPlugin = new InjectManifest(workboxOptions);
|
|
51
|
+
|
|
52
|
+
// https://github.com/GoogleChrome/workbox/issues/1790#issuecomment-1241356293
|
|
53
|
+
if (env === 'development') {
|
|
54
|
+
Object.defineProperty(workboxPlugin, 'alreadyCalled', {
|
|
55
|
+
get() {
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
set() {},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Fix `ERROR in Invalid URL` problem
|
|
63
|
+
// https://github.com/webpack/webpack/issues/9570#issuecomment-520713006
|
|
64
|
+
if (sourceMap) {
|
|
65
|
+
config.output.set('devtoolNamespace', 'tramvai');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
config.plugin('workbox').use(workboxPlugin);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (pwa.webmanifest?.enabled) {
|
|
72
|
+
const webmanifestPlugin = new WebManifestPlugin(pwa.webmanifest);
|
|
73
|
+
|
|
74
|
+
config.plugin('webmanifest').use(webmanifestPlugin);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (pwa.icon?.src) {
|
|
78
|
+
const iconSrc = path.join(rootDir, root, pwa.icon.src);
|
|
79
|
+
const pwaIconsPlugin = new PwaIconsPlugin({ ...pwa.icon, src: iconSrc });
|
|
80
|
+
|
|
81
|
+
config.plugin('pwa-icons').use(pwaIconsPlugin);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type Config from 'webpack-chain';
|
|
2
|
+
import type { ConfigManager } from '../../../../config/configManager';
|
|
3
|
+
import type { ApplicationConfigEntry } from '../../../../typings/configEntry/application';
|
|
4
|
+
import { pwaSharedBlock } from './shared';
|
|
5
|
+
|
|
6
|
+
export const pwaBlock =
|
|
7
|
+
(configManager: ConfigManager<ApplicationConfigEntry>) => (config: Config) => {
|
|
8
|
+
config.batch(pwaSharedBlock(configManager));
|
|
9
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import type Config from 'webpack-chain';
|
|
3
|
+
import type { ConfigManager } from '../../../../config/configManager';
|
|
4
|
+
import type { ApplicationConfigEntry } from '../../../../typings/configEntry/application';
|
|
5
|
+
|
|
6
|
+
export const pwaSharedBlock =
|
|
7
|
+
(configManager: ConfigManager<ApplicationConfigEntry>) => (config: Config) => {
|
|
8
|
+
const {
|
|
9
|
+
experiments: { pwa },
|
|
10
|
+
rootDir,
|
|
11
|
+
root,
|
|
12
|
+
} = configManager;
|
|
13
|
+
|
|
14
|
+
config.plugin('define').tap((args) => [
|
|
15
|
+
{
|
|
16
|
+
...args[0],
|
|
17
|
+
'process.env.TRAMVAI_PWA_WORKBOX_ENABLED': JSON.stringify(pwa.workbox?.enabled),
|
|
18
|
+
// @todo duplicated logic with path.join
|
|
19
|
+
'process.env.TRAMVAI_PWA_SW_SRC': JSON.stringify(path.join(rootDir, root, pwa.sw?.src)),
|
|
20
|
+
'process.env.TRAMVAI_PWA_SW_DEST': JSON.stringify(pwa.sw?.dest),
|
|
21
|
+
'process.env.TRAMVAI_PWA_SW_SCOPE': JSON.stringify(pwa.sw?.scope),
|
|
22
|
+
'process.env.TRAMVAI_PWA_MANIFEST_ENABLED': JSON.stringify(pwa.webmanifest?.enabled),
|
|
23
|
+
'process.env.TRAMVAI_PWA_MANIFEST_DEST': JSON.stringify(pwa.webmanifest?.dest),
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type webpack from 'webpack';
|
|
2
|
+
import type { Compiler } from 'webpack';
|
|
3
|
+
import type Sharp from 'sharp';
|
|
4
|
+
import type { PwaIconOptions } from '../../../typings/pwa';
|
|
5
|
+
|
|
6
|
+
const pluginName = 'PwaIconsPlugin';
|
|
7
|
+
|
|
8
|
+
export class PwaIconsPlugin implements webpack.WebpackPluginInstance {
|
|
9
|
+
private hash: string;
|
|
10
|
+
|
|
11
|
+
constructor(private options: PwaIconOptions) {
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
apply(compiler: Compiler) {
|
|
16
|
+
const { webpack } = compiler;
|
|
17
|
+
const { Compilation } = webpack;
|
|
18
|
+
const { RawSource } = webpack.sources;
|
|
19
|
+
const { src, dest, sizes } = this.options;
|
|
20
|
+
|
|
21
|
+
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
|
|
22
|
+
// watch icon source file
|
|
23
|
+
if (!compilation.fileDependencies.has(src)) {
|
|
24
|
+
compilation.fileDependencies.add(src);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
compilation.hooks.processAssets.tapPromise(
|
|
28
|
+
{
|
|
29
|
+
name: pluginName,
|
|
30
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
|
|
31
|
+
},
|
|
32
|
+
async () => {
|
|
33
|
+
try {
|
|
34
|
+
const sharp: typeof Sharp = require('sharp');
|
|
35
|
+
// check icon source file updates
|
|
36
|
+
const nextHash = await new Promise<string>((resolve) => {
|
|
37
|
+
compilation.fileSystemInfo.getFileHash(src, (_, hash) => {
|
|
38
|
+
resolve(hash);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!this.hash) {
|
|
43
|
+
this.hash = nextHash;
|
|
44
|
+
} else if (this.hash === nextHash) {
|
|
45
|
+
// skip if icon source file not changed
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// @todo persistent cache between builds !!!
|
|
50
|
+
const promises = sizes.map((size) => {
|
|
51
|
+
return sharp(src)
|
|
52
|
+
.resize({ width: size, height: size })
|
|
53
|
+
.png({
|
|
54
|
+
quality: 95,
|
|
55
|
+
compressionLevel: 8,
|
|
56
|
+
adaptiveFiltering: true,
|
|
57
|
+
progressive: true,
|
|
58
|
+
force: true,
|
|
59
|
+
})
|
|
60
|
+
.toBuffer()
|
|
61
|
+
.then((content) => ({
|
|
62
|
+
filename: `${dest}/${size}x${size}.png`,
|
|
63
|
+
content,
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const results = await Promise.all(promises);
|
|
68
|
+
|
|
69
|
+
results.forEach(({ filename, content }) => {
|
|
70
|
+
const source = new RawSource(content);
|
|
71
|
+
const asset = compilation.getAsset(filename);
|
|
72
|
+
|
|
73
|
+
if (asset) {
|
|
74
|
+
compilation.updateAsset(filename, source);
|
|
75
|
+
} else {
|
|
76
|
+
compilation.emitAsset(filename, source);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// @todo: throw error?
|
|
81
|
+
console.error('PWA icons processing error:', e);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type webpack from 'webpack';
|
|
2
|
+
import type { Compiler } from 'webpack';
|
|
3
|
+
import type { WebManifestOptions } from '../../../typings/pwa';
|
|
4
|
+
|
|
5
|
+
const pluginName = 'WebManifestPlugin';
|
|
6
|
+
|
|
7
|
+
export class WebManifestPlugin implements webpack.WebpackPluginInstance {
|
|
8
|
+
constructor(private options: WebManifestOptions) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
apply(compiler: Compiler) {
|
|
13
|
+
const { webpack } = compiler;
|
|
14
|
+
const { Compilation } = webpack;
|
|
15
|
+
const { RawSource } = webpack.sources;
|
|
16
|
+
const { dest, enabled, ...content } = this.options;
|
|
17
|
+
|
|
18
|
+
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
|
|
19
|
+
compilation.hooks.processAssets.tap(
|
|
20
|
+
{
|
|
21
|
+
name: pluginName,
|
|
22
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
|
|
23
|
+
},
|
|
24
|
+
() => {
|
|
25
|
+
const manifestFilename = dest;
|
|
26
|
+
|
|
27
|
+
compilation.emitAsset(manifestFilename, new RawSource(JSON.stringify(content)));
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|