@modern-js/builder 2.5.0 → 2.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # @modern-js/builder
2
2
 
3
+ ## 2.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dfece9dc1c: fix(builder): vendor library chunks include sources
8
+ fix(builder): 用户源码被划分到第三方库所在 Chunk
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies [206c806efa]
13
+ - Updated dependencies [0f15fc597c]
14
+ - Updated dependencies [5f899af53a]
15
+ - Updated dependencies [dcad887024]
16
+ - Updated dependencies [a4672f7c16]
17
+ - Updated dependencies [ebe0d2dd6e]
18
+ - Updated dependencies [7fff9020e1]
19
+ - Updated dependencies [84bfb439b8]
20
+ - @modern-js/builder-shared@2.7.0
21
+ - @modern-js/utils@2.7.0
22
+
23
+ ## 2.6.0
24
+
25
+ ### Patch Changes
26
+
27
+ - 107f674: feat(builder): add dev.beforeStartUrl config
28
+
29
+ feat(builder): 新增 dev.beforeStartUrl 配置项
30
+
31
+ - 0fe658a: feat(builder): support passing URL to html.favicon
32
+
33
+ feat(builder): 支持在 html.favicon 中直接传入 URL
34
+
35
+ - Updated dependencies [b92d6db]
36
+ - Updated dependencies [e1f799e]
37
+ - Updated dependencies [107f674]
38
+ - Updated dependencies [7915ab3]
39
+ - Updated dependencies [03d7f7d]
40
+ - Updated dependencies [fae9d1b]
41
+ - Updated dependencies [0fe658a]
42
+ - Updated dependencies [62930b9]
43
+ - @modern-js/builder-shared@2.6.0
44
+ - @modern-js/utils@2.6.0
45
+
3
46
  ## 2.5.0
4
47
 
5
48
  ### Patch Changes
@@ -0,0 +1,3 @@
1
+ import type { DefaultBuilderPlugin, SharedBuilderPluginAPI } from '@modern-js/builder-shared';
2
+ export declare const applyInjectTags: (api: SharedBuilderPluginAPI) => void;
3
+ export declare const builderPluginHtml: () => DefaultBuilderPlugin;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.builderPluginHtml = exports.applyInjectTags = void 0;
30
+ const path_1 = __importDefault(require("path"));
31
+ const builder_shared_1 = require("@modern-js/builder-shared");
32
+ const lodash_1 = __importDefault(require("@modern-js/utils/lodash"));
33
+ async function getTemplateParameters(entryName, config, assetPrefix) {
34
+ const { applyOptionsChain } = await Promise.resolve().then(() => __importStar(require('@modern-js/utils')));
35
+ const { mountId, templateParameters, templateParametersByEntries } = config.html;
36
+ const meta = await (0, builder_shared_1.getMetaTags)(entryName, config);
37
+ const title = (0, builder_shared_1.getTitle)(entryName, config);
38
+ const templateParams = (templateParametersByEntries === null || templateParametersByEntries === void 0 ? void 0 : templateParametersByEntries[entryName]) || templateParameters;
39
+ const baseParameters = {
40
+ meta,
41
+ title,
42
+ mountId,
43
+ entryName,
44
+ assetPrefix,
45
+ };
46
+ return (compilation, assets, assetTags, pluginOptions) => {
47
+ const defaultOptions = {
48
+ compilation,
49
+ webpackConfig: compilation.options,
50
+ htmlWebpackPlugin: {
51
+ tags: assetTags,
52
+ files: assets,
53
+ options: pluginOptions,
54
+ },
55
+ ...baseParameters,
56
+ };
57
+ return applyOptionsChain(defaultOptions, templateParams);
58
+ };
59
+ }
60
+ async function getChunks(entryName, entryValue) {
61
+ const { isPlainObject } = await Promise.resolve().then(() => __importStar(require('@modern-js/utils')));
62
+ const dependOn = [];
63
+ if (isPlainObject(entryValue)) {
64
+ // @ts-expect-error assume entry is an entry object
65
+ dependOn.push(...entryValue.dependOn);
66
+ }
67
+ return [...dependOn, entryName];
68
+ }
69
+ // Determine if the string is a favicon url
70
+ const isFaviconUrl = (str) => str.startsWith('http');
71
+ const applyInjectTags = (api) => {
72
+ api.modifyBundlerChain(async (chain, { HtmlPlugin, CHAIN_ID }) => {
73
+ const config = api.getNormalizedConfig();
74
+ const tags = lodash_1.default.castArray(config.html.tags).filter(Boolean);
75
+ const tagsByEntries = lodash_1.default.mapValues(config.html.tagsByEntries, tags => lodash_1.default.castArray(tags).filter(Boolean));
76
+ const shouldByEntries = lodash_1.default.some(tagsByEntries, 'length');
77
+ // skip if options is empty.
78
+ if (!tags.length && !shouldByEntries) {
79
+ return;
80
+ }
81
+ // dynamic import.
82
+ const { HtmlTagsPlugin } = await Promise.resolve().then(() => __importStar(require('@modern-js/builder-shared')));
83
+ // const { HtmlTagsPlugin } = await import('../webpackPlugins/HtmlTagsPlugin');
84
+ // create shared options used for entry without specified options.
85
+ const sharedOptions = {
86
+ HtmlPlugin,
87
+ append: true,
88
+ hash: false,
89
+ publicPath: true,
90
+ tags,
91
+ };
92
+ // apply only one webpack plugin if `html.tagsByEntries` is empty.
93
+ if (tags.length && !shouldByEntries) {
94
+ chain
95
+ .plugin(CHAIN_ID.PLUGIN.HTML_TAGS)
96
+ .use(HtmlTagsPlugin, [sharedOptions]);
97
+ return;
98
+ }
99
+ // apply webpack plugin for each entries.
100
+ for (const [entry, filename] of Object.entries(api.getHTMLPaths())) {
101
+ const opts = { ...sharedOptions, includes: [filename] };
102
+ entry in tagsByEntries && (opts.tags = tagsByEntries[entry]);
103
+ chain
104
+ .plugin(`${CHAIN_ID.PLUGIN.HTML_TAGS}#${entry}`)
105
+ .use(HtmlTagsPlugin, [opts]);
106
+ }
107
+ });
108
+ };
109
+ exports.applyInjectTags = applyInjectTags;
110
+ const builderPluginHtml = () => ({
111
+ name: 'builder-plugin-html',
112
+ setup(api) {
113
+ const routesInfo = [];
114
+ api.modifyBundlerChain(async (chain, { HtmlPlugin, isProd, CHAIN_ID, target }) => {
115
+ const config = api.getNormalizedConfig();
116
+ // if html is disabled or target is server, skip html plugin
117
+ if ((0, builder_shared_1.isHtmlDisabled)(config, target)) {
118
+ return;
119
+ }
120
+ const { removeTailSlash, applyOptionsChain } = await Promise.resolve().then(() => __importStar(require('@modern-js/utils')));
121
+ const minify = (0, builder_shared_1.getMinify)(isProd, config);
122
+ const assetPrefix = removeTailSlash(chain.output.get('publicPath') || '');
123
+ const entries = chain.entryPoints.entries() || {};
124
+ const entryNames = Object.keys(entries);
125
+ const htmlPaths = api.getHTMLPaths();
126
+ const faviconUrls = [];
127
+ await Promise.all(entryNames.map(async (entryName, index) => {
128
+ const entryValue = entries[entryName].values();
129
+ const chunks = await getChunks(entryName, entryValue);
130
+ const inject = (0, builder_shared_1.getInject)(entryName, config);
131
+ const favicon = (0, builder_shared_1.getFavicon)(entryName, config);
132
+ const filename = htmlPaths[entryName];
133
+ const template = (0, builder_shared_1.getTemplatePath)(entryName, config);
134
+ const templateParameters = await getTemplateParameters(entryName, config, assetPrefix);
135
+ const pluginOptions = {
136
+ chunks,
137
+ inject,
138
+ minify,
139
+ filename,
140
+ template,
141
+ templateParameters,
142
+ };
143
+ if (favicon) {
144
+ if (isFaviconUrl(favicon)) {
145
+ faviconUrls.push({
146
+ filename,
147
+ url: favicon,
148
+ });
149
+ }
150
+ else {
151
+ // HTMLWebpackPlugin only support favicon file path
152
+ pluginOptions.favicon = favicon;
153
+ }
154
+ }
155
+ const finalOptions = applyOptionsChain(pluginOptions, config.tools.htmlPlugin, {
156
+ entryName,
157
+ entryValue,
158
+ });
159
+ routesInfo.push({
160
+ urlPath: index === 0 ? '/' : `/${entryName}`,
161
+ entryName,
162
+ entryPath: filename,
163
+ isSPA: true,
164
+ });
165
+ chain
166
+ .plugin(`${CHAIN_ID.PLUGIN.HTML}-${entryName}`)
167
+ .use(HtmlPlugin, [finalOptions]);
168
+ }));
169
+ if (config.html) {
170
+ const { appIcon, crossorigin } = config.html;
171
+ if (crossorigin) {
172
+ const { HtmlCrossOriginPlugin } = await Promise.resolve().then(() => __importStar(require('@modern-js/builder-shared')));
173
+ const formattedCrossorigin = crossorigin === true ? 'anonymous' : crossorigin;
174
+ chain
175
+ .plugin(CHAIN_ID.PLUGIN.HTML_CROSS_ORIGIN)
176
+ .use(HtmlCrossOriginPlugin, [
177
+ { crossOrigin: formattedCrossorigin, HtmlPlugin },
178
+ ]);
179
+ // todo: not support in rspack
180
+ // @ts-expect-error
181
+ chain.output.crossOriginLoading(formattedCrossorigin);
182
+ }
183
+ if (faviconUrls.length) {
184
+ const { HtmlFaviconUrlPlugin } = await Promise.resolve().then(() => __importStar(require('@modern-js/builder-shared')));
185
+ chain
186
+ .plugin(CHAIN_ID.PLUGIN.FAVICON_URL)
187
+ .use(HtmlFaviconUrlPlugin, [{ faviconUrls, HtmlPlugin }]);
188
+ }
189
+ if (appIcon) {
190
+ const { HtmlAppIconPlugin } = await Promise.resolve().then(() => __importStar(require('@modern-js/builder-shared')));
191
+ const distDir = (0, builder_shared_1.getDistPath)(config.output, 'image');
192
+ const iconPath = path_1.default.isAbsolute(appIcon)
193
+ ? appIcon
194
+ : path_1.default.join(api.context.rootPath, appIcon);
195
+ chain
196
+ .plugin(CHAIN_ID.PLUGIN.APP_ICON)
197
+ .use(HtmlAppIconPlugin, [{ iconPath, distDir, HtmlPlugin }]);
198
+ }
199
+ }
200
+ });
201
+ api.onBeforeStartDevServer(async () => {
202
+ const { fs, ROUTE_SPEC_FILE } = await Promise.resolve().then(() => __importStar(require('@modern-js/utils')));
203
+ const routeFilePath = path_1.default.join(api.context.distPath, ROUTE_SPEC_FILE);
204
+ // generate a basic route.json for modern.js dev server
205
+ // if the framework has already generate a route.json, do nothing
206
+ if (!(await (0, builder_shared_1.isFileExists)(routeFilePath)) && routesInfo.length) {
207
+ await fs.outputFile(routeFilePath, JSON.stringify({ routes: routesInfo }, null, 2));
208
+ }
209
+ });
210
+ (0, exports.applyInjectTags)(api);
211
+ },
212
+ });
213
+ exports.builderPluginHtml = builderPluginHtml;
@@ -1,4 +1,5 @@
1
1
  export declare const plugins: {
2
+ html: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
2
3
  cleanOutput: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
3
4
  startUrl: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
4
5
  fileSize: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
@@ -10,6 +11,7 @@ export declare const plugins: {
10
11
  toml: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
11
12
  svg: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
12
13
  splitChunks: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
14
+ inlineChunk: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
13
15
  bundleAnalyzer: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
14
16
  font: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
15
17
  image: () => Promise<import("@modern-js/builder-shared").DefaultBuilderPlugin>;
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.plugins = void 0;
27
27
  const builder_shared_1 = require("@modern-js/builder-shared");
28
28
  exports.plugins = {
29
+ html: () => Promise.resolve().then(() => __importStar(require('./html'))).then(m => m.builderPluginHtml()),
29
30
  cleanOutput: () => Promise.resolve().then(() => __importStar(require('./cleanOutput'))).then(m => m.builderPluginCleanOutput()),
30
31
  startUrl: () => Promise.resolve().then(() => __importStar(require('./startUrl'))).then(m => m.builderPluginStartUrl()),
31
32
  fileSize: () => Promise.resolve().then(() => __importStar(require('./fileSize'))).then(m => m.builderPluginFileSize()),
@@ -37,6 +38,7 @@ exports.plugins = {
37
38
  toml: () => Promise.resolve().then(() => __importStar(require('./toml'))).then(m => m.builderPluginToml()),
38
39
  svg: () => Promise.resolve().then(() => __importStar(require('./svg'))).then(m => m.builderPluginSvg()),
39
40
  splitChunks: () => Promise.resolve().then(() => __importStar(require('./splitChunks'))).then(m => m.builderPluginSplitChunks()),
41
+ inlineChunk: () => Promise.resolve().then(() => __importStar(require('./inlineChunk'))).then(m => m.builderPluginInlineChunk()),
40
42
  bundleAnalyzer: () => Promise.resolve().then(() => __importStar(require('./bundleAnalyzer'))).then(m => m.builderPluginBundleAnalyzer()),
41
43
  font: () => Promise.resolve().then(() => __importStar(require('./asset'))).then(m => m.builderAssetPlugin('font', builder_shared_1.FONT_EXTENSIONS)),
42
44
  image: () => Promise.resolve().then(() => __importStar(require('./asset'))).then(m => m.builderAssetPlugin('image', builder_shared_1.IMAGE_EXTENSIONS)),
@@ -0,0 +1,2 @@
1
+ import { DefaultBuilderPlugin } from '@modern-js/builder-shared';
2
+ export declare const builderPluginInlineChunk: () => DefaultBuilderPlugin;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.builderPluginInlineChunk = void 0;
27
+ const builder_shared_1 = require("@modern-js/builder-shared");
28
+ const builderPluginInlineChunk = () => ({
29
+ name: 'builder-plugin-inline-chunk',
30
+ setup(api) {
31
+ api.modifyBundlerChain(async (chain, { target, CHAIN_ID, isProd, HtmlPlugin }) => {
32
+ const config = api.getNormalizedConfig();
33
+ if ((0, builder_shared_1.isHtmlDisabled)(config, target) || !isProd) {
34
+ return;
35
+ }
36
+ const { InlineChunkHtmlPlugin } = await Promise.resolve().then(() => __importStar(require('@modern-js/builder-shared')));
37
+ const { disableInlineRuntimeChunk, enableInlineStyles,
38
+ // todo: not support enableInlineScripts in rspack yet, which will take unknown build error
39
+ enableInlineScripts, } = config.output;
40
+ chain.plugin(CHAIN_ID.PLUGIN.INLINE_HTML).use(InlineChunkHtmlPlugin, [
41
+ HtmlPlugin,
42
+ {
43
+ tests: [
44
+ enableInlineScripts && /\.js$/,
45
+ enableInlineStyles && /\.css$/,
46
+ !disableInlineRuntimeChunk &&
47
+ // RegExp like /builder-runtime([.].+)?\.js$/
48
+ // matches builder-runtime.js and builder-runtime.123456.js
49
+ new RegExp(`${builder_shared_1.RUNTIME_CHUNK_NAME}([.].+)?\\.js$`),
50
+ ].filter(Boolean),
51
+ distPath: (0, builder_shared_1.pick)(config.output.distPath, ['js', 'css']),
52
+ },
53
+ ]);
54
+ });
55
+ },
56
+ });
57
+ exports.builderPluginInlineChunk = builderPluginInlineChunk;
@@ -1,2 +1,4 @@
1
1
  import { DefaultBuilderPlugin } from '@modern-js/builder-shared';
2
+ /** Expect to match path just like "./node_modules/react-router/" */
3
+ export declare const createDependenciesRegExp: (...dependencies: string[]) => RegExp;
2
4
  export declare function builderPluginSplitChunks(): DefaultBuilderPlugin;
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.builderPluginSplitChunks = void 0;
29
+ exports.builderPluginSplitChunks = exports.createDependenciesRegExp = void 0;
30
30
  const assert_1 = __importDefault(require("assert"));
31
31
  const builder_shared_1 = require("@modern-js/builder-shared");
32
32
  function getUserDefinedCacheGroups(forceSplitting) {
@@ -43,28 +43,31 @@ function getUserDefinedCacheGroups(forceSplitting) {
43
43
  });
44
44
  return cacheGroups;
45
45
  }
46
+ /** Expect to match path just like "./node_modules/react-router/" */
47
+ const createDependenciesRegExp = (...dependencies) => new RegExp(`[\\\\/]node_modules[\\\\/](${dependencies.join('|')})[\\\\/]`);
48
+ exports.createDependenciesRegExp = createDependenciesRegExp;
46
49
  async function splitByExperience(ctx) {
47
50
  const { isPackageInstalled } = await Promise.resolve().then(() => __importStar(require('@modern-js/utils')));
48
51
  const { override, polyfill, rootPath, defaultConfig, userDefinedCacheGroups, } = ctx;
49
52
  const experienceCacheGroup = {};
50
53
  const packageRegExps = {
51
- react: /[\\/]react|react-dom[\\/]/,
52
- router: /[\\/]react-router|react-router-dom|history[\\/]/,
53
- lodash: /[\\/]lodash|lodash-es[\\/]/,
54
+ react: (0, exports.createDependenciesRegExp)('react', 'react-dom'),
55
+ router: (0, exports.createDependenciesRegExp)('react-router', 'react-router-dom', 'history'),
56
+ lodash: (0, exports.createDependenciesRegExp)('lodash', 'lodash-es'),
54
57
  };
55
58
  // Detect if the package is installed in current project
56
59
  // If installed, add the package to cache group
57
60
  if (isPackageInstalled('antd', rootPath)) {
58
- packageRegExps.antd = /[\\/]antd[\\/]/;
61
+ packageRegExps.antd = (0, exports.createDependenciesRegExp)('antd');
59
62
  }
60
63
  if (isPackageInstalled('@arco-design/web-react', rootPath)) {
61
- packageRegExps.arco = /[\\/]arco-design[\\/]/;
64
+ packageRegExps.arco = (0, exports.createDependenciesRegExp)('arco-design');
62
65
  }
63
66
  if (isPackageInstalled('@douyinfe/semi-ui', rootPath)) {
64
- packageRegExps.semi = /[\\/]semi-ui[\\/]/;
67
+ packageRegExps.semi = (0, exports.createDependenciesRegExp)('semi-ui');
65
68
  }
66
69
  if (polyfill === 'entry' || polyfill === 'usage') {
67
- packageRegExps.polyfill = /[\\/]core-js|@babel\/runtime[\\/]/;
70
+ packageRegExps.polyfill = (0, exports.createDependenciesRegExp)('core-js', '@babel/runtime');
68
71
  }
69
72
  Object.entries(packageRegExps).forEach(([name, test]) => {
70
73
  const key = `lib-${name}`;
@@ -44,7 +44,7 @@ function builderPluginStartUrl() {
44
44
  return;
45
45
  }
46
46
  const config = api.getNormalizedConfig();
47
- const { https, startUrl } = config.dev;
47
+ const { https, startUrl, beforeStartUrl } = config.dev;
48
48
  if (!startUrl) {
49
49
  return;
50
50
  }
@@ -56,16 +56,25 @@ function builderPluginStartUrl() {
56
56
  else {
57
57
  urls.push(...lodash_1.default.castArray(startUrl).map(item => (0, exports.replacePlaceholder)(item, port)));
58
58
  }
59
+ const { ensureArray } = await Promise.resolve().then(() => __importStar(require('@modern-js/utils')));
59
60
  const { openBrowser } = await Promise.resolve().then(() => __importStar(require('@modern-js/builder-shared')));
60
- for (const url of urls) {
61
- /**
62
- * If a URL has been opened in current process, we will not open it again.
63
- * It can prevent opening the same URL multiple times.
64
- */
65
- if (!openedURLs.includes(url)) {
66
- await openBrowser(url);
67
- openedURLs.push(url);
61
+ const openUrls = () => {
62
+ for (const url of urls) {
63
+ /**
64
+ * If a URL has been opened in current process, we will not open it again.
65
+ * It can prevent opening the same URL multiple times.
66
+ */
67
+ if (!openedURLs.includes(url)) {
68
+ openBrowser(url);
69
+ openedURLs.push(url);
70
+ }
68
71
  }
72
+ };
73
+ if (beforeStartUrl) {
74
+ Promise.all(ensureArray(beforeStartUrl).map(fn => fn())).then(openUrls);
75
+ }
76
+ else {
77
+ openUrls();
69
78
  }
70
79
  });
71
80
  },
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "engines": {
15
15
  "node": ">=14.0.0"
16
16
  },
17
- "version": "2.5.0",
17
+ "version": "2.7.0",
18
18
  "jsnext:source": "./src/index.ts",
19
19
  "types": "./dist/index.d.ts",
20
20
  "main": "./dist/index.js",
@@ -27,8 +27,8 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@svgr/webpack": "6.5.1",
30
- "@modern-js/builder-shared": "2.5.0",
31
- "@modern-js/utils": "2.5.0"
30
+ "@modern-js/builder-shared": "2.7.0",
31
+ "@modern-js/utils": "2.7.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@babel/core": "7.18.0",
@@ -36,7 +36,7 @@
36
36
  "@types/babel__preset-env": "^7.9.2",
37
37
  "@types/node": "^14",
38
38
  "typescript": "^4",
39
- "@scripts/vitest-config": "2.5.0"
39
+ "@scripts/vitest-config": "2.7.0"
40
40
  },
41
41
  "publishConfig": {
42
42
  "registry": "https://registry.npmjs.org/",