@pinnacle0/webpack-util 0.3.23 → 0.3.26

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.
Files changed (139) hide show
  1. package/config/jest.config.ts +35 -0
  2. package/config/tsconfig.script.json +10 -0
  3. package/config/tsconfig.src.json +12 -0
  4. package/config/tsconfig.test.json +11 -0
  5. package/package.json +6 -8
  6. package/script/build.ts +65 -0
  7. package/script/format.ts +7 -0
  8. package/src/CanadyarnRunner.ts +35 -0
  9. package/src/CodeStyleChecker.ts +44 -0
  10. package/src/{Constant.js → Constant.ts} +1 -5
  11. package/src/CoreUtil.ts +24 -0
  12. package/src/ProjectStructureChecker.ts +119 -0
  13. package/src/TestRunner.ts +28 -0
  14. package/src/TypescriptTypeChecker.ts +29 -0
  15. package/src/{WebpackBuilder.js → WebpackBuilder.ts} +71 -51
  16. package/src/WebpackConfigGenerator/{ConfigEntryDescriptorsFactory.js → ConfigEntryDescriptorsFactory.ts} +36 -29
  17. package/src/WebpackConfigGenerator/HTMLWebpackPluginsFactory.ts +24 -0
  18. package/src/WebpackConfigGenerator/Plugin/css.plugin.ts +31 -0
  19. package/src/WebpackConfigGenerator/Plugin/html.plugin.ts +47 -0
  20. package/src/WebpackConfigGenerator/Plugin/index.ts +33 -0
  21. package/src/WebpackConfigGenerator/Plugin/{moment.plugin.js → moment.plugin.ts} +5 -12
  22. package/src/WebpackConfigGenerator/Plugin/script-tag-crossorigin-plugin.ts +22 -0
  23. package/src/WebpackConfigGenerator/Plugin/ts.plugin.ts +29 -0
  24. package/src/WebpackConfigGenerator/Plugin/webpack.plugin.ts +21 -0
  25. package/src/WebpackConfigGenerator/Rule/{RegExpUtil.js → RegExpUtil.ts} +8 -14
  26. package/src/WebpackConfigGenerator/Rule/{core-fe-hmr-babel-plugin.js → core-fe-hmr-babel-plugin.ts} +43 -9
  27. package/src/WebpackConfigGenerator/Rule/{image.rule.js → image.rule.ts} +5 -8
  28. package/src/WebpackConfigGenerator/Rule/index.ts +17 -0
  29. package/src/WebpackConfigGenerator/Rule/other.rule.ts +23 -0
  30. package/src/WebpackConfigGenerator/Rule/{stylesheet.rule.js → stylesheet.rule.ts} +34 -30
  31. package/src/WebpackConfigGenerator/Rule/{ts.rule.js → ts.rule.ts} +19 -17
  32. package/src/WebpackConfigGenerator/{WebpackConfigSerializationUtil.js → WebpackConfigSerializationUtil.ts} +23 -22
  33. package/src/WebpackConfigGenerator/WebpackEntryFactory.ts +22 -0
  34. package/src/WebpackConfigGenerator/WebpackOutputPublicURLFactory.ts +22 -0
  35. package/src/WebpackConfigGenerator/{WebpackResolveAliasFactory.js → WebpackResolveAliasFactory.ts} +16 -13
  36. package/src/WebpackConfigGenerator/WebpackResolveExtensionsFactory.ts +19 -0
  37. package/src/WebpackConfigGenerator/{WebpackResolveModulesFactory.js → WebpackResolveModulesFactory.ts} +9 -7
  38. package/src/WebpackConfigGenerator/index.ts +213 -0
  39. package/src/WebpackServerStarter.ts +138 -0
  40. package/src/index.ts +9 -0
  41. package/src/{type.d.ts → type.ts} +8 -4
  42. package/test/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin/__snapshots__/shouldIgnore.test.ts.snap +21 -0
  43. package/test/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin/__snapshots__/shouldTransform.test.ts.snap +53 -0
  44. package/test/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin/shouldIgnore.test.ts +36 -0
  45. package/test/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin/shouldTransform.test.ts +72 -0
  46. package/test/WebpackConfigGenerator/plugin/fixture/script.js +2 -0
  47. package/test/WebpackConfigGenerator/plugin/fixture/script1.js +1 -0
  48. package/test/WebpackConfigGenerator/plugin/script-tag-crossorigin-plugin.test.ts +58 -0
  49. package/test/test-project/package.json +5 -0
  50. package/tsconfig.json +15 -0
  51. package/src/CanadyarnRunner.d.ts +0 -14
  52. package/src/CanadyarnRunner.js +0 -38
  53. package/src/CanadyarnRunner.js.map +0 -1
  54. package/src/CodeStyleChecker.d.ts +0 -10
  55. package/src/CodeStyleChecker.js +0 -44
  56. package/src/CodeStyleChecker.js.map +0 -1
  57. package/src/Constant.d.ts +0 -21
  58. package/src/Constant.js.map +0 -1
  59. package/src/CoreUtil.d.ts +0 -11
  60. package/src/CoreUtil.js +0 -26
  61. package/src/CoreUtil.js.map +0 -1
  62. package/src/ProjectStructureChecker.d.ts +0 -18
  63. package/src/ProjectStructureChecker.js +0 -113
  64. package/src/ProjectStructureChecker.js.map +0 -1
  65. package/src/TestRunner.d.ts +0 -8
  66. package/src/TestRunner.js +0 -30
  67. package/src/TestRunner.js.map +0 -1
  68. package/src/TypescriptTypeChecker.d.ts +0 -8
  69. package/src/TypescriptTypeChecker.js +0 -25
  70. package/src/TypescriptTypeChecker.js.map +0 -1
  71. package/src/WebpackBuilder.d.ts +0 -38
  72. package/src/WebpackBuilder.js.map +0 -1
  73. package/src/WebpackConfigGenerator/ConfigEntryDescriptorsFactory.d.ts +0 -13
  74. package/src/WebpackConfigGenerator/ConfigEntryDescriptorsFactory.js.map +0 -1
  75. package/src/WebpackConfigGenerator/HTMLWebpackPluginsFactory.d.ts +0 -9
  76. package/src/WebpackConfigGenerator/HTMLWebpackPluginsFactory.js +0 -20
  77. package/src/WebpackConfigGenerator/HTMLWebpackPluginsFactory.js.map +0 -1
  78. package/src/WebpackConfigGenerator/Plugin/css.plugin.d.ts +0 -16
  79. package/src/WebpackConfigGenerator/Plugin/css.plugin.js +0 -33
  80. package/src/WebpackConfigGenerator/Plugin/css.plugin.js.map +0 -1
  81. package/src/WebpackConfigGenerator/Plugin/html.plugin.d.ts +0 -16
  82. package/src/WebpackConfigGenerator/Plugin/html.plugin.js +0 -48
  83. package/src/WebpackConfigGenerator/Plugin/html.plugin.js.map +0 -1
  84. package/src/WebpackConfigGenerator/Plugin/index.d.ts +0 -27
  85. package/src/WebpackConfigGenerator/Plugin/index.js +0 -32
  86. package/src/WebpackConfigGenerator/Plugin/index.js.map +0 -1
  87. package/src/WebpackConfigGenerator/Plugin/moment.plugin.d.ts +0 -10
  88. package/src/WebpackConfigGenerator/Plugin/moment.plugin.js.map +0 -1
  89. package/src/WebpackConfigGenerator/Plugin/script-tag-crossorigin-plugin.d.ts +0 -4
  90. package/src/WebpackConfigGenerator/Plugin/script-tag-crossorigin-plugin.js +0 -28
  91. package/src/WebpackConfigGenerator/Plugin/script-tag-crossorigin-plugin.js.map +0 -1
  92. package/src/WebpackConfigGenerator/Plugin/ts.plugin.d.ts +0 -16
  93. package/src/WebpackConfigGenerator/Plugin/ts.plugin.js +0 -31
  94. package/src/WebpackConfigGenerator/Plugin/ts.plugin.js.map +0 -1
  95. package/src/WebpackConfigGenerator/Plugin/webpack.plugin.d.ts +0 -14
  96. package/src/WebpackConfigGenerator/Plugin/webpack.plugin.js +0 -24
  97. package/src/WebpackConfigGenerator/Plugin/webpack.plugin.js.map +0 -1
  98. package/src/WebpackConfigGenerator/Rule/RegExpUtil.d.ts +0 -4
  99. package/src/WebpackConfigGenerator/Rule/RegExpUtil.js.map +0 -1
  100. package/src/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin.d.ts +0 -13
  101. package/src/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin.js.map +0 -1
  102. package/src/WebpackConfigGenerator/Rule/image.rule.d.ts +0 -9
  103. package/src/WebpackConfigGenerator/Rule/image.rule.js.map +0 -1
  104. package/src/WebpackConfigGenerator/Rule/index.d.ts +0 -13
  105. package/src/WebpackConfigGenerator/Rule/index.js +0 -18
  106. package/src/WebpackConfigGenerator/Rule/index.js.map +0 -1
  107. package/src/WebpackConfigGenerator/Rule/other.rule.d.ts +0 -12
  108. package/src/WebpackConfigGenerator/Rule/other.rule.js +0 -22
  109. package/src/WebpackConfigGenerator/Rule/other.rule.js.map +0 -1
  110. package/src/WebpackConfigGenerator/Rule/stylesheet.rule.d.ts +0 -17
  111. package/src/WebpackConfigGenerator/Rule/stylesheet.rule.js.map +0 -1
  112. package/src/WebpackConfigGenerator/Rule/ts.rule.d.ts +0 -18
  113. package/src/WebpackConfigGenerator/Rule/ts.rule.js.map +0 -1
  114. package/src/WebpackConfigGenerator/WebpackConfigSerializationUtil.d.ts +0 -10
  115. package/src/WebpackConfigGenerator/WebpackConfigSerializationUtil.js.map +0 -1
  116. package/src/WebpackConfigGenerator/WebpackEntryFactory.d.ts +0 -9
  117. package/src/WebpackConfigGenerator/WebpackEntryFactory.js +0 -14
  118. package/src/WebpackConfigGenerator/WebpackEntryFactory.js.map +0 -1
  119. package/src/WebpackConfigGenerator/WebpackOutputPublicURLFactory.d.ts +0 -11
  120. package/src/WebpackConfigGenerator/WebpackOutputPublicURLFactory.js +0 -16
  121. package/src/WebpackConfigGenerator/WebpackOutputPublicURLFactory.js.map +0 -1
  122. package/src/WebpackConfigGenerator/WebpackResolveAliasFactory.d.ts +0 -11
  123. package/src/WebpackConfigGenerator/WebpackResolveAliasFactory.js.map +0 -1
  124. package/src/WebpackConfigGenerator/WebpackResolveExtensionsFactory.d.ts +0 -7
  125. package/src/WebpackConfigGenerator/WebpackResolveExtensionsFactory.js +0 -16
  126. package/src/WebpackConfigGenerator/WebpackResolveExtensionsFactory.js.map +0 -1
  127. package/src/WebpackConfigGenerator/WebpackResolveModulesFactory.d.ts +0 -7
  128. package/src/WebpackConfigGenerator/WebpackResolveModulesFactory.js.map +0 -1
  129. package/src/WebpackConfigGenerator/index.d.ts +0 -31
  130. package/src/WebpackConfigGenerator/index.js +0 -191
  131. package/src/WebpackConfigGenerator/index.js.map +0 -1
  132. package/src/WebpackServerStarter.d.ts +0 -28
  133. package/src/WebpackServerStarter.js +0 -111
  134. package/src/WebpackServerStarter.js.map +0 -1
  135. package/src/index.d.ts +0 -10
  136. package/src/index.js +0 -8
  137. package/src/index.js.map +0 -1
  138. package/src/type.js +0 -3
  139. package/src/type.js.map +0 -1
@@ -0,0 +1,213 @@
1
+ import {Utility} from "@pinnacle0/devtool-util";
2
+ import path from "path";
3
+ import type webpack from "webpack";
4
+ import {Constant} from "../Constant";
5
+ import {CoreUtil} from "../CoreUtil";
6
+ import type {EntryDescriptor, WebpackConfigGeneratorOptions} from "../type";
7
+ import {ConfigEntryDescriptorsFactory} from "./ConfigEntryDescriptorsFactory";
8
+ import {HTMLWebpackPluginsFactory} from "./HTMLWebpackPluginsFactory";
9
+ import {WebpackConfigSerializationUtil} from "./WebpackConfigSerializationUtil";
10
+ import {WebpackEntryFactory} from "./WebpackEntryFactory";
11
+ import {WebpackOutputPublicURLFactory} from "./WebpackOutputPublicURLFactory";
12
+ import {WebpackResolveAliasFactory} from "./WebpackResolveAliasFactory";
13
+ import {WebpackResolveExtensionsFactory} from "./WebpackResolveExtensionsFactory";
14
+ import {WebpackResolveModulesFactory} from "./WebpackResolveModulesFactory";
15
+ import {Plugin} from "./Plugin";
16
+ import {Rule} from "./Rule";
17
+
18
+ /**
19
+ * Generates a webpack config with sane defaults.
20
+ */
21
+ export class WebpackConfigGenerator {
22
+ private readonly env: string | null;
23
+ private readonly projectDirectory: string;
24
+ private readonly projectSrcDirectory: string;
25
+ private readonly tsconfigFilepath: string;
26
+ private readonly enableProfiling: boolean;
27
+ private readonly maxEntryPointKiloByte: number;
28
+ private readonly maxAssetKiloByte: number;
29
+ private readonly isFastMode: boolean;
30
+ private readonly verbose: boolean;
31
+ private readonly defineVars: {[key: string]: string};
32
+ private readonly extraExtensionsForOtherRule: string[];
33
+
34
+ private readonly configEntryDescriptors: EntryDescriptor[];
35
+ private readonly entry: NonNullable<webpack.Configuration["entry"]>;
36
+ private readonly htmlWebpackPluginInstances: NonNullable<webpack.Configuration["plugins"]>;
37
+ private readonly resolveExtensions: NonNullable<NonNullable<webpack.Configuration["resolve"]>["extensions"]>;
38
+ private readonly resolveModules: NonNullable<NonNullable<webpack.Configuration["resolve"]>["modules"]>;
39
+ private readonly resolveAliases: NonNullable<NonNullable<webpack.Configuration["resolve"]>["alias"]>;
40
+ private readonly outputPublicPath: string;
41
+
42
+ private readonly logger = Utility.createConsoleLogger("WebpackConfigGenerator");
43
+
44
+ constructor(private readonly options: WebpackConfigGeneratorOptions) {
45
+ this.env = CoreUtil.currentEnv();
46
+ this.projectDirectory = options.projectDirectory;
47
+ this.projectSrcDirectory = path.join(options.projectDirectory, "src");
48
+ this.tsconfigFilepath = path.join(options.projectDirectory, "tsconfig.json");
49
+
50
+ this.enableProfiling = CoreUtil.profilingEnabled();
51
+ this.isFastMode = CoreUtil.isFastMode();
52
+ this.maxEntryPointKiloByte = options.maxEntryPointKiloByte ?? Constant.maxEntryPointKiloByte;
53
+ this.maxAssetKiloByte = options.maxAssetKiloByte ?? Constant.maxAssetKiloByte;
54
+ this.verbose = options.verbose || false;
55
+ this.defineVars = options.defineVars || {};
56
+ this.extraExtensionsForOtherRule = options.extraExtensionsForOtherRule || [];
57
+
58
+ this.configEntryDescriptors = ConfigEntryDescriptorsFactory.generate({
59
+ indexName: options.indexName || "index",
60
+ projectSrcDirectory: this.projectSrcDirectory,
61
+ extraEntries: options.extraEntries || {},
62
+ });
63
+ this.entry = WebpackEntryFactory.generate({
64
+ configEntryDescriptors: this.configEntryDescriptors,
65
+ });
66
+ this.htmlWebpackPluginInstances = HTMLWebpackPluginsFactory.generate({
67
+ configEntryDescriptors: this.configEntryDescriptors,
68
+ });
69
+ this.outputPublicPath = WebpackOutputPublicURLFactory.generate({
70
+ env: this.env,
71
+ webpackPublicPath: options.webpackPublicPath,
72
+ });
73
+ this.resolveExtensions = WebpackResolveExtensionsFactory.generate({
74
+ prioritizedExtensionPrefixes: options.prioritizedExtensionPrefixes,
75
+ });
76
+ this.resolveModules = WebpackResolveModulesFactory.generate({
77
+ projectSrcDirectory: this.projectSrcDirectory,
78
+ });
79
+ this.resolveAliases = WebpackResolveAliasFactory.generate({
80
+ env: this.env,
81
+ resolvers: options.dynamicPathResolvers ?? [],
82
+ });
83
+
84
+ this.logger.info("Config constructed:");
85
+ for (const info of [
86
+ `-- Code Checking: ${this.isFastMode ? "Minimal Check" : "Default"}`,
87
+ `-- Env: ${this.env || "[N/A]"}`,
88
+ `-- Src Directory: ${this.projectSrcDirectory}`,
89
+ `-- HTML Entries: ${Object.keys(this.entry).join(" / ")}`,
90
+ `-- Webpack Public URL: ${this.outputPublicPath}`,
91
+ `-- Dynamic Aliases: ${JSON.stringify(this.resolveAliases, null, 4)}`,
92
+ `-- Use Cache: ${this.env === null}`,
93
+ ]) {
94
+ console.info(info);
95
+ }
96
+ }
97
+
98
+ development(): webpack.Configuration {
99
+ const config: webpack.Configuration = {
100
+ mode: "development",
101
+ entry: this.entry,
102
+ target: "web", // https://github.com/webpack/webpack-dev-server/issues/2758 Hot-reload will break if we provide list to target
103
+ output: {
104
+ filename: "static/js/[name].js",
105
+ publicPath: "/",
106
+ },
107
+ resolve: {
108
+ extensions: this.resolveExtensions,
109
+ modules: this.resolveModules,
110
+ alias: this.resolveAliases,
111
+ },
112
+ devtool: "inline-cheap-module-source-map",
113
+ optimization: {
114
+ usedExports: true,
115
+ splitChunks: {
116
+ automaticNameDelimiter: "-",
117
+ maxAsyncRequests: 30,
118
+ },
119
+ },
120
+ module: {
121
+ rules: [
122
+ Rule.ts({tsconfigFilepath: this.tsconfigFilepath, fastRefresh: true}),
123
+ Rule.stylesheet({minimize: false}),
124
+ Rule.image(),
125
+ Rule.other({extraExtensionsForOtherRule: this.extraExtensionsForOtherRule}),
126
+ // prettier-format-preserve
127
+ ],
128
+ },
129
+ plugins: [
130
+ ...this.htmlWebpackPluginInstances,
131
+ Plugin.ignoreMomentLocale(),
132
+ Plugin.reactRefresh(),
133
+ Plugin.webpack.progress({enableProfiling: false}),
134
+ Plugin.webpack.define(this.defineVars),
135
+ // prettier-format-preserve
136
+ ],
137
+ cache:
138
+ this.env === null
139
+ ? {
140
+ type: "filesystem",
141
+ cacheDirectory: path.join(this.projectDirectory, ".webpack-cache"),
142
+ }
143
+ : false,
144
+ };
145
+ if (this.verbose || CoreUtil.verbose()) {
146
+ this.logger.info("Full webpack config:");
147
+ console.info(WebpackConfigSerializationUtil.configToString(config));
148
+ }
149
+ return config;
150
+ }
151
+
152
+ production(outputDirectory: string): webpack.Configuration {
153
+ const config: webpack.Configuration = {
154
+ mode: "production",
155
+ entry: this.entry,
156
+ target: ["web", "es5"],
157
+ output: {
158
+ path: outputDirectory,
159
+ filename: this.enableProfiling ? "static/js/[name].js" : pathInfo => this.configEntryDescriptors.find(_ => _.name === pathInfo.chunk!.name)!.outputFilename,
160
+ chunkFilename: this.enableProfiling ? "static/js/[id].[name].js" : "static/js/[id].[chunkhash:8].js",
161
+ publicPath: this.outputPublicPath,
162
+ crossOriginLoading: "anonymous",
163
+ },
164
+ resolve: {
165
+ extensions: this.resolveExtensions,
166
+ modules: this.resolveModules,
167
+ alias: this.resolveAliases,
168
+ },
169
+ bail: true,
170
+ optimization: {
171
+ splitChunks: {
172
+ automaticNameDelimiter: "-",
173
+ maxAsyncRequests: 30,
174
+ },
175
+ minimizer: [
176
+ Plugin.minimizer.terser({sourceMap: true}),
177
+ Plugin.minimizer.css(),
178
+ // prettier-format-preserve
179
+ ],
180
+ },
181
+ performance: {
182
+ maxEntrypointSize: this.enableProfiling ? Number.MAX_SAFE_INTEGER : this.maxEntryPointKiloByte * 1000,
183
+ maxAssetSize: this.maxAssetKiloByte * 1000,
184
+ assetFilter: (filename: string) => Constant.mediaExtensions.concat(this.extraExtensionsForOtherRule).every(_ => !filename.endsWith(_)),
185
+ },
186
+ module: {
187
+ rules: [
188
+ Rule.ts({tsconfigFilepath: this.tsconfigFilepath, fastRefresh: false}),
189
+ Rule.stylesheet({minimize: true}),
190
+ Rule.image(),
191
+ Rule.other({extraExtensionsForOtherRule: this.extraExtensionsForOtherRule}),
192
+ // prettier-format-preserve
193
+ ],
194
+ },
195
+ plugins: [
196
+ ...this.htmlWebpackPluginInstances,
197
+ Plugin.scriptTagCrossOriginPlugin(),
198
+ Plugin.ignoreMomentLocale(),
199
+ Plugin.fileOutput.miniCssExtract({enableProfiling: this.enableProfiling}),
200
+ ...(this.enableProfiling ? [Plugin.webpack.progress({enableProfiling: true})] : []), // disable to not bloat up CI logs
201
+ Plugin.webpack.define(this.defineVars),
202
+ // prettier-format-preserve
203
+ ],
204
+ };
205
+ if (this.verbose || CoreUtil.verbose()) {
206
+ this.logger.info("Full webpack config:");
207
+ console.info(WebpackConfigSerializationUtil.configToString(config));
208
+ }
209
+ return config;
210
+ }
211
+ }
212
+
213
+ export type {WebpackConfigGeneratorOptions};
@@ -0,0 +1,138 @@
1
+ import {Utility} from "@pinnacle0/devtool-util";
2
+ import path from "path";
3
+ import webpack from "webpack";
4
+ import DevServer from "webpack-dev-server";
5
+ import type {Application as ExpressApplication} from "express";
6
+ import type {WebpackConfigGeneratorOptions} from "./WebpackConfigGenerator";
7
+ import {WebpackConfigGenerator} from "./WebpackConfigGenerator";
8
+ import type WebpackDevServer from "webpack-dev-server";
9
+
10
+ export interface WebpackServerStarterOptions
11
+ extends Pick<
12
+ WebpackConfigGeneratorOptions,
13
+ // prettier-reserve
14
+ "projectDirectory" | "dynamicPathResolvers" | "extraEntries" | "prioritizedExtensionPrefixes" | "defineVars" | "extraExtensionsForOtherRule"
15
+ > {
16
+ port: number;
17
+ apiProxy?: {
18
+ target: string;
19
+ context: string[];
20
+ };
21
+ interceptExpressApp?: (app: ExpressApplication) => void;
22
+ }
23
+
24
+ /**
25
+ * Start webpack dev server, by creating WebpackServerStarter instance and then run.
26
+ *
27
+ ***************************************
28
+ *
29
+ * Add "--env envName" to command line, if you want to switch config folder dynamically.
30
+ */
31
+
32
+ export class WebpackServerStarter {
33
+ private readonly devServerConfigContentBase: string;
34
+ private readonly onBeforeSetupMiddleware: WebpackDevServer.Configuration["onBeforeSetupMiddleware"];
35
+ private readonly port: number;
36
+ private readonly apiProxy:
37
+ | {
38
+ target: string;
39
+ context: string[];
40
+ secure: boolean;
41
+ changeOrigin: boolean;
42
+ }[]
43
+ | undefined;
44
+ private readonly logger = Utility.createConsoleLogger("WebpackServerStarter");
45
+ private readonly webpackConfig: webpack.Configuration;
46
+
47
+ constructor({
48
+ projectDirectory,
49
+ port,
50
+ apiProxy,
51
+ interceptExpressApp,
52
+ dynamicPathResolvers,
53
+ extraEntries,
54
+ prioritizedExtensionPrefixes,
55
+ defineVars,
56
+ extraExtensionsForOtherRule,
57
+ }: WebpackServerStarterOptions) {
58
+ this.devServerConfigContentBase = path.join(projectDirectory, "static");
59
+ this.port = port;
60
+ this.apiProxy = apiProxy
61
+ ? [
62
+ {
63
+ context: apiProxy.context,
64
+ target: apiProxy.target,
65
+ secure: false,
66
+ changeOrigin: true,
67
+ },
68
+ ]
69
+ : undefined;
70
+ this.onBeforeSetupMiddleware = interceptExpressApp ? devServer => (devServer.app ? interceptExpressApp(devServer.app) : undefined) : undefined;
71
+ this.webpackConfig = new WebpackConfigGenerator({
72
+ projectDirectory,
73
+ dynamicPathResolvers,
74
+ extraEntries,
75
+ prioritizedExtensionPrefixes,
76
+ defineVars,
77
+ extraExtensionsForOtherRule,
78
+ }).development();
79
+ }
80
+
81
+ async run() {
82
+ try {
83
+ this.logger.info(["Starting dev server on port", String(this.port)]);
84
+ const server = this.createDevServerInstance();
85
+ const signals: NodeJS.Signals[] = ["SIGINT", "SIGTERM"];
86
+ for (const signal of signals) {
87
+ process.on(signal, () => {
88
+ // force stop webpack dev server
89
+ server.stopCallback(() => {});
90
+ process.exit();
91
+ });
92
+ }
93
+ await server.start();
94
+ } catch (e) {
95
+ this.logger.error(e);
96
+ console.error(e);
97
+ process.exit(1);
98
+ }
99
+ }
100
+
101
+ private createDevServerInstance() {
102
+ return new DevServer(
103
+ {
104
+ host: "0.0.0.0",
105
+ port: this.port,
106
+ static: {
107
+ directory: this.devServerConfigContentBase,
108
+ },
109
+ historyApiFallback: true,
110
+ server: {
111
+ type: "https",
112
+ },
113
+ compress: true,
114
+ hot: true,
115
+ client: {
116
+ overlay: {
117
+ errors: true,
118
+ },
119
+ },
120
+ onBeforeSetupMiddleware: this.onBeforeSetupMiddleware,
121
+ devMiddleware: {
122
+ stats: {
123
+ colors: true,
124
+ // https://github.com/webpack/webpack/blob/b65d060040a26255cbf6f50350fef4d4ffcce4d7/lib/stats/DefaultStatsPresetPlugin.js#L96-L103
125
+ all: false,
126
+ errors: true,
127
+ errorsCount: true,
128
+ warnings: true,
129
+ warningsCount: true,
130
+ logging: "warn",
131
+ },
132
+ },
133
+ proxy: this.apiProxy,
134
+ },
135
+ webpack(this.webpackConfig)
136
+ );
137
+ }
138
+ }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ import {WebpackBuilder} from "./WebpackBuilder";
2
+ import {WebpackServerStarter} from "./WebpackServerStarter";
3
+ import {CoreUtil} from "./CoreUtil";
4
+
5
+ export const WebpackUtil = Object.freeze({
6
+ Builder: WebpackBuilder,
7
+ ServerStarter: WebpackServerStarter,
8
+ ...CoreUtil,
9
+ });
@@ -1,4 +1,5 @@
1
1
  import type webpack from "webpack";
2
+
2
3
  export interface WebpackConfigGeneratorOptions {
3
4
  /**
4
5
  * Directory of containing the application code.
@@ -88,12 +89,11 @@ export interface WebpackConfigGeneratorOptions {
88
89
  /**
89
90
  * Ref: https://webpack.js.org/plugins/define-plugin/
90
91
  */
91
- defineVars?: {
92
- [key: string]: string;
93
- } | undefined;
92
+ defineVars?: {[key: string]: string} | undefined;
94
93
  onSuccess?: () => void;
95
94
  extraExtensionsForOtherRule?: string[] | undefined;
96
95
  }
96
+
97
97
  export interface EntryDescriptor {
98
98
  name: string;
99
99
  /**
@@ -114,6 +114,7 @@ export interface EntryDescriptor {
114
114
  */
115
115
  htmlPath?: string;
116
116
  }
117
+
117
118
  /**
118
119
  * Same as EntryDescriptor, but guaranteed to have an htmlPath.
119
120
  * Used for creating HTMLWebpackPlugin instances.
@@ -124,6 +125,7 @@ export interface HTMLEntryDescriptor {
124
125
  entryPath: string;
125
126
  htmlPath: string;
126
127
  }
128
+
127
129
  export interface DynamicPathResolver {
128
130
  /**
129
131
  * Prefix of the dependencies that should be aliased.
@@ -142,6 +144,7 @@ export interface DynamicPathResolver {
142
144
  */
143
145
  resolver: string | null | ((env: string | null) => string | null);
144
146
  }
147
+
145
148
  export interface InternalCheckerOptions {
146
149
  /**
147
150
  * Directory of containing the application code.
@@ -160,6 +163,7 @@ export interface InternalCheckerOptions {
160
163
  */
161
164
  extraCheckDirectories?: string[];
162
165
  }
166
+
163
167
  declare module "webpack" {
164
168
  interface TapablePlugin {
165
169
  apply(...args: any[]): void;
@@ -176,7 +180,7 @@ declare module "webpack" {
176
180
  * This is a workaround to allow "@types/*-plugin" packages to work before their
177
181
  * type definitions are properly upgraded.
178
182
  */
179
- abstract class Plugin {
183
+ export abstract class Plugin {
180
184
  apply(compiler: webpack.Compiler): void;
181
185
  }
182
186
  }
@@ -0,0 +1,21 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`core-fe-hmr-babel-plugin sanity test for some react component files 1`] = `
4
+ "import React from \\"react\\";
5
+ import { ReactUtil } from \\"@pinnacle0/util\\";
6
+ export const Main = ReactUtil.memo(\\"Main\\", () => {
7
+ return React.createElement(\\"div\\", {});
8
+ });"
9
+ `;
10
+
11
+ exports[`core-fe-hmr-babel-plugin should ignore when non core-fe source files 1`] = `
12
+ "import { Module } from \\"not-core-fe\\";
13
+ import { Main } from \\"./component/Main\\";
14
+ const initialState = {};
15
+
16
+ class FeatureModule extends Module {}
17
+
18
+ const featureModule = register(new FeatureModule(\\"feature\\", initialState));
19
+ export const actions = featureModule.getActions();
20
+ export const MainComponent = featureModule.attachLifecycle(Main);"
21
+ `;
@@ -0,0 +1,53 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`core-fe-hmr-babel-plugin works with \`import * as CoreFENamespace\` 1`] = `
4
+ "import * as CoreFENamespace from \\"core-fe\\";
5
+ import { Main } from \\"./component/Main\\";
6
+ const initialState = {};
7
+
8
+ class FeatureModule extends CoreFENamespace.Module {}
9
+
10
+ const featureModule = register(new FeatureModule(\\"feature\\", initialState));
11
+ export const actions = featureModule.getActions();
12
+ export const MainComponent = featureModule.attachLifecycle(Main);
13
+ if (module.hot) module.hot.decline();"
14
+ `;
15
+
16
+ exports[`core-fe-hmr-babel-plugin works with \`import {Module as CoreFEModule}\` 1`] = `
17
+ "import { Module as CoreFEModule } from \\"core-fe\\";
18
+ import { Main } from \\"./component/Main\\";
19
+ const initialState = {};
20
+
21
+ class FeatureModule extends CoreFEModule {}
22
+
23
+ const featureModule = register(new FeatureModule(\\"feature\\", initialState));
24
+ export const actions = featureModule.getActions();
25
+ export const MainComponent = featureModule.attachLifecycle(Main);
26
+ if (module.hot) module.hot.decline();"
27
+ `;
28
+
29
+ exports[`core-fe-hmr-babel-plugin works with \`import {Module}\` 1`] = `
30
+ "import { Module } from \\"core-fe\\";
31
+ import { Main } from \\"./component/Main\\";
32
+ const initialState = {};
33
+
34
+ class FeatureModule extends Module {}
35
+
36
+ const featureModule = register(new FeatureModule(\\"feature\\", initialState));
37
+ export const actions = featureModule.getActions();
38
+ export const MainComponent = featureModule.attachLifecycle(Main);
39
+ if (module.hot) module.hot.decline();"
40
+ `;
41
+
42
+ exports[`core-fe-hmr-babel-plugin works with \`import CoreFEDefault, {Module}\` 1`] = `
43
+ "import CoreFEDefault, { Module } from \\"core-fe\\";
44
+ import { Main } from \\"./component/Main\\";
45
+ const initialState = {};
46
+
47
+ class FeatureModule extends Module {}
48
+
49
+ const featureModule = register(new FeatureModule(\\"feature\\", initialState));
50
+ export const actions = featureModule.getActions();
51
+ export const MainComponent = featureModule.attachLifecycle(Main);
52
+ if (module.hot) module.hot.decline();"
53
+ `;
@@ -0,0 +1,36 @@
1
+ import * as babel from "@babel/core";
2
+ import plugin from "@pinnacle0/webpack-util/src/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin";
3
+
4
+ describe("core-fe-hmr-babel-plugin", () => {
5
+ test("should ignore when non core-fe source files", () => {
6
+ const source = `
7
+ import {Module} from "not-core-fe";
8
+ import {Main} from "./component/Main";
9
+
10
+ const initialState = {};
11
+ class FeatureModule extends Module {}
12
+
13
+ const featureModule = register(new FeatureModule("feature", initialState));
14
+ export const actions = featureModule.getActions();
15
+ export const MainComponent = featureModule.attachLifecycle(Main);
16
+ `;
17
+ const {code} = babel.transformSync(source, {plugins: [plugin]})!;
18
+ expect(code).toMatchSnapshot();
19
+ expect(code).not.toContain("if (module.hot) module.hot.decline();");
20
+ });
21
+
22
+ test("sanity test for some react component files", () => {
23
+ const source = `
24
+ import React from "react";
25
+ import {ReactUtil} from "@pinnacle0/util";
26
+
27
+ export const Main = ReactUtil.memo("Main", () => {
28
+ return React.createElement("div", {});
29
+ });
30
+ `;
31
+ const {code} = babel.transformSync(source, {plugins: [plugin]})!;
32
+
33
+ expect(code).toMatchSnapshot();
34
+ expect(code).not.toContain("if (module.hot) module.hot.decline();");
35
+ });
36
+ });
@@ -0,0 +1,72 @@
1
+ import * as babel from "@babel/core";
2
+ import plugin from "@pinnacle0/webpack-util/src/WebpackConfigGenerator/Rule/core-fe-hmr-babel-plugin";
3
+
4
+ describe("core-fe-hmr-babel-plugin", () => {
5
+ test("works with `import {Module}`", () => {
6
+ const source = `
7
+ import {Module} from "core-fe";
8
+ import {Main} from "./component/Main";
9
+
10
+ const initialState = {};
11
+ class FeatureModule extends Module {}
12
+
13
+ const featureModule = register(new FeatureModule("feature", initialState));
14
+ export const actions = featureModule.getActions();
15
+ export const MainComponent = featureModule.attachLifecycle(Main);
16
+ `;
17
+ const {code} = babel.transformSync(source, {plugins: [plugin]})!;
18
+ expect(code).toMatchSnapshot();
19
+ expect(code).toContain("if (module.hot) module.hot.decline();");
20
+ });
21
+
22
+ test("works with `import {Module as CoreFEModule}`", () => {
23
+ const source = `
24
+ import {Module as CoreFEModule} from "core-fe";
25
+ import {Main} from "./component/Main";
26
+
27
+ const initialState = {};
28
+ class FeatureModule extends CoreFEModule {}
29
+
30
+ const featureModule = register(new FeatureModule("feature", initialState));
31
+ export const actions = featureModule.getActions();
32
+ export const MainComponent = featureModule.attachLifecycle(Main);
33
+ `;
34
+ const {code} = babel.transformSync(source, {plugins: [plugin]})!;
35
+ expect(code).toMatchSnapshot();
36
+ expect(code).toContain("if (module.hot) module.hot.decline();");
37
+ });
38
+
39
+ test("works with `import CoreFEDefault, {Module}`", () => {
40
+ const source = `
41
+ import CoreFEDefault, {Module} from "core-fe";
42
+ import {Main} from "./component/Main";
43
+
44
+ const initialState = {};
45
+ class FeatureModule extends Module {}
46
+
47
+ const featureModule = register(new FeatureModule("feature", initialState));
48
+ export const actions = featureModule.getActions();
49
+ export const MainComponent = featureModule.attachLifecycle(Main);
50
+ `;
51
+ const {code} = babel.transformSync(source, {plugins: [plugin]})!;
52
+ expect(code).toMatchSnapshot();
53
+ expect(code).toContain("if (module.hot) module.hot.decline();");
54
+ });
55
+
56
+ test("works with `import * as CoreFENamespace`", () => {
57
+ const source = `
58
+ import * as CoreFENamespace from "core-fe";
59
+ import {Main} from "./component/Main";
60
+
61
+ const initialState = {};
62
+ class FeatureModule extends CoreFENamespace.Module {}
63
+
64
+ const featureModule = register(new FeatureModule("feature", initialState));
65
+ export const actions = featureModule.getActions();
66
+ export const MainComponent = featureModule.attachLifecycle(Main);
67
+ `;
68
+ const {code} = babel.transformSync(source, {plugins: [plugin]})!;
69
+ expect(code).toMatchSnapshot();
70
+ expect(code).toContain("if (module.hot) module.hot.decline();");
71
+ });
72
+ });
@@ -0,0 +1,2 @@
1
+ const {a} = require("./script1");
2
+ console.info(a);
@@ -0,0 +1 @@
1
+ module.exports = {a: "123"};
@@ -0,0 +1,58 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import webpack from "webpack";
4
+ import HtmlWebpackPlugin from "html-webpack-plugin";
5
+ import {ScriptTagCrossOriginPlugin} from "../../../src/WebpackConfigGenerator/Plugin/script-tag-crossorigin-plugin";
6
+
7
+ const OUTPUT_DIR = path.join(__dirname, "./dist");
8
+ type Entry = Omit<webpack.Configuration["entry"], "undefined">;
9
+ const createConfig = (entry: Entry): webpack.Configuration => ({
10
+ mode: "production",
11
+ entry,
12
+ output: {
13
+ path: OUTPUT_DIR,
14
+ },
15
+ plugins: [
16
+ new HtmlWebpackPlugin({
17
+ filename: "index.html",
18
+ }),
19
+ new ScriptTagCrossOriginPlugin(),
20
+ ],
21
+ });
22
+
23
+ describe("script-tag-crossorigin-plugin test: Add crossorigin='anonymous'", () => {
24
+ beforeEach(done => {
25
+ fs.rmSync(OUTPUT_DIR, {force: true, recursive: true});
26
+ done();
27
+ });
28
+
29
+ const testPlugin = (entry: Entry, expectedResult: RegExp, done: () => void) => {
30
+ const config: webpack.Configuration = createConfig(entry);
31
+ webpack(config, error => {
32
+ expect(error).toBeFalsy();
33
+ const outputFile = fs.readFileSync(path.join(OUTPUT_DIR, "index.html"));
34
+ expect(outputFile.toString()).toMatch(expectedResult);
35
+ done();
36
+ });
37
+ };
38
+
39
+ test("single entry script tag", done => {
40
+ testPlugin(
41
+ {
42
+ main: path.join(__dirname, "./fixture/script.js"),
43
+ },
44
+ new RegExp('<script.+src.+\\main.js.+crossorigin="anonymous"', "gm"),
45
+ done
46
+ );
47
+ });
48
+ test("multiple entry script tag", done => {
49
+ testPlugin(
50
+ {
51
+ main: path.join(__dirname, "./fixture/script.js"),
52
+ second: path.join(__dirname, "./fixture/script.js"),
53
+ },
54
+ new RegExp('<script.+src.+\\main.js.+crossorigin="anonymous".*>.+<script.+src.+second\\.js.+crossorigin="anonymous"', "gm"),
55
+ done
56
+ );
57
+ });
58
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@pinnacle0/webpack-util-test-project",
3
+ "license": "UNLICENSED",
4
+ "private": true
5
+ }