@arkxio/ark-dev-utils 0.1.6 → 0.1.9

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/package.json CHANGED
@@ -1,51 +1,51 @@
1
- {
2
- "name": "@arkxio/ark-dev-utils",
3
- "version": "0.1.6",
4
- "description": "ark dev utils",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/arkxos/arkjs.git",
8
- "directory": "packages/ark-dev-utils"
9
- },
10
- "license": "MIT",
11
- "author": {
12
- "name": "fantasticsoul"
13
- },
14
- "main": "lib/index.js",
15
- "files": [
16
- "dist",
17
- "lib",
18
- "src",
19
- "typings.d.ts",
20
- "jsconfig.json",
21
- ".babelrc.js",
22
- "README.md"
23
- ],
24
- "scripts": {
25
- "build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
26
- "build:commonjs": "cross-env BUILD_ENV=commonjs rollup -c",
27
- "build:es": "cross-env BUILD_ENV=es rollup -c",
28
- "build:umd": "cross-env BUILD_ENV=development rollup -c",
29
- "build:umd:min": "cross-env BUILD_ENV=production rollup -c"
30
- },
31
- "dependencies": {
32
- "jsdom": ">=19.0.0"
33
- },
34
- "devDependencies": {
35
- "@babel/preset-env": "^7.12.11",
36
- "@babel/preset-react": "^7.12.10",
37
- "babel-cli": "^6.26.0",
38
- "fs-extra": "^10.1.0",
39
- "rollup": "^2.23.0"
40
- },
41
- "peerDependencies": {
42
- "jsdom": ">=19.0.0",
43
- "webpack": "5.75.0"
44
- },
45
- "bundleDependencies": false,
46
- "publishConfig": {
47
- "access": "public",
48
- "registry": "https://registry.npmjs.org/"
49
- },
50
- "deprecated": false
51
- }
1
+ {
2
+ "name": "@arkxio/ark-dev-utils",
3
+ "version": "0.1.9",
4
+ "description": "ark dev utils",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/arkxos/arkjs.git",
8
+ "directory": "packages/ark-dev-utils"
9
+ },
10
+ "license": "MIT",
11
+ "author": {
12
+ "name": "fantasticsoul"
13
+ },
14
+ "main": "lib/index.js",
15
+ "files": [
16
+ "dist",
17
+ "lib",
18
+ "src",
19
+ "typings.d.ts",
20
+ "jsconfig.json",
21
+ ".babelrc.js",
22
+ "README.md"
23
+ ],
24
+ "scripts": {
25
+ "build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
26
+ "build:commonjs": "cross-env BUILD_ENV=commonjs rollup -c",
27
+ "build:es": "cross-env BUILD_ENV=es rollup -c",
28
+ "build:umd": "cross-env BUILD_ENV=development rollup -c",
29
+ "build:umd:min": "cross-env BUILD_ENV=production rollup -c"
30
+ },
31
+ "dependencies": {
32
+ "jsdom": ">=19.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@babel/preset-env": "^7.12.11",
36
+ "@babel/preset-react": "^7.12.10",
37
+ "babel-cli": "^6.26.0",
38
+ "fs-extra": "^10.1.0",
39
+ "rollup": "^2.23.0"
40
+ },
41
+ "peerDependencies": {
42
+ "jsdom": ">=19.0.0",
43
+ "webpack": "5.75.0"
44
+ },
45
+ "bundleDependencies": false,
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "registry": "https://registry.npmjs.org/"
49
+ },
50
+ "deprecated": false
51
+ }
@@ -1,135 +1,125 @@
1
- import path from "path";
2
- import extractArkMetaJson from './meta-extractor/index';
3
- import {verbose} from "./inner-utils";
4
- import { Compilation } from 'webpack';
5
- import { resetScriptIdx } from './meta-extractor/fillAssetList';
6
-
7
- export default class ArkWebpackPlugin {
8
-
9
- constructor(options) {
10
- this.metaOptions = options;
11
- }
12
-
13
- apply(compiler) {
14
- // 获取项目根目录路径
15
- const projectRoot = compiler.context;
16
- const useExtractOptions = this.metaOptions;
17
-
18
- // 在每次编译开始时重置脚本索引计数器,确保文件名一致性
19
- compiler.hooks.thisCompilation.tap('ArkResetScriptIdx', () => {
20
- resetScriptIdx();
21
- });
22
-
23
- compiler.hooks.thisCompilation.tap('ArkDynamicCdnChunkLoaderPlugin', (compilation) => {
24
- compilation.hooks.processAssets.tap(
25
- {
26
- name: 'ArkDynamicCdnChunkLoaderPlugin',
27
- stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
28
- },
29
- (assets) => {
30
- // 新增分块文件过滤逻辑
31
- const chunkFiles = new Set();
32
- compilation.chunks.forEach(chunk => {
33
- chunk.files.forEach(file => {
34
- if (file.endsWith('.js')) chunkFiles.add(file);
35
- });
36
- });
37
- // 调试日志
38
- verbose('[分块文件列表]', Array.from(chunkFiles));
39
-
40
- Object.entries(assets).forEach(([filename, source]) => {
41
- // 仅处理分块JS文件
42
- if (!chunkFiles.has(filename)) return;
43
-
44
- // 检查资源内容有效性
45
- if (
46
- !source ||
47
- !source.source ||
48
- typeof source.source() !== 'string' ||
49
- source.source().trim() === ''
50
- ) {
51
- console.warn(`资源 ${filename} 内容为空或无效,跳过处理`);
52
- return;
53
- }
54
-
55
- let content = source.source().toString();
56
-
57
- // 替换逻辑
58
- // /(__webpack_require__\.l)\(url,/g)
59
- // const webpackRequireLPattern = /(__webpack_require__\.l)\(url,/g;
60
- // if (webpackRequireLPattern.test(content)) {
61
- // verbose('检测到 __webpack_require__.l 模式');
62
- // content = content.replace(
63
- // webpackRequireLPattern,
64
- // `(function(url, done, key, chunkId) {
65
- // if (typeof window.ArkConfig !== 'undefined' && window.ArkConfig.cdnHost) {
66
- // if (!url.startsWith(window.ArkConfig.cdnHost) && url.startsWith('https://unpkg.com')) {
67
- // url = window.ArkConfig.cdnHost + url.slice('https://unpkg.com'.length);
68
- // }
69
- // }
70
- // return $1(url, done, key, chunkId);
71
- // })(url,`
72
- // );
73
- const webpackRequireLPattern = /"(https:\/\/unpkg\.com)/g;
74
- if (webpackRequireLPattern.test(content)) {
75
- verbose('检测到 unpkg.com 模式');
76
- content = content.replace(
77
- webpackRequireLPattern,
78
- `window.ArkConfig.cdnHost + "`
79
- );
80
-
81
- // 更新资源
82
- compilation.updateAsset(
83
- filename,
84
- new compiler.webpack.sources.RawSource(content)
85
- );
86
- }
87
-
88
- });
89
- }
90
- );
91
- });
92
-
93
- compiler.hooks.emit.tapPromise('ArkMetaJsonGeneratorPlugin', async (compilation) => {
94
- // 注入环境判断脚本
95
- const isDev = process.env.NODE_ENV === 'development';
96
-
97
- // 收集所有生成的JS文件
98
- const outputPath = compilation.options.output.publicPath || '';
99
- const jsFiles = [];
100
-
101
- // Webpack 5 正确的API:使用 getAssets()
102
- const assets = compilation.getAssets ? compilation.getAssets() : compilation.assets;
103
-
104
- // 遍历所有资产,收集JS文件
105
- assets.forEach((asset) => {
106
- const assetName = asset.name || asset;
107
- if (assetName.endsWith('.js')) {
108
- console.log('Found JS file:', assetName);
109
- // 排除 ark-meta.json ark_userChunk 文件
110
- if (assetName.indexOf('js/app.') !== -1 ||
111
- assetName.indexOf('js/runtime.') !== -1 ||
112
- assetName.indexOf('js/vendors.') !== -1 ||
113
- assetName.indexOf('js/main.') !== -1) {
114
- jsFiles.push(outputPath + assetName);
115
- }
116
- }
117
- });
118
-
119
- console.log('Final JS files for chunkJsSrcList:', jsFiles);
120
-
121
- // 创建 customAssets 数组用于收集自定义资产(如从 HTML 内联 script 提取的代码)
122
- const customAssets = [];
123
-
124
- const arkMeta = await extractArkMetaJson({
125
- ...useExtractOptions,
126
- chunkJsFiles: jsFiles,
127
- isDev: isDev,
128
- compilation: compilation,
129
- customAssets: customAssets
130
- });
131
-
132
- return Promise.resolve();
133
- });
134
- }
135
- }
1
+ import path from "path";
2
+ import extractArkMetaJson from './meta-extractor/index';
3
+ import {verbose} from "./inner-utils";
4
+ import { Compilation } from 'webpack';
5
+ import { resetScriptIdx } from './meta-extractor/fillAssetList';
6
+
7
+ export default class ArkWebpackPlugin {
8
+
9
+ constructor(options) {
10
+ this.metaOptions = options;
11
+ }
12
+
13
+ apply(compiler) {
14
+ // 获取项目根目录路径
15
+ const projectRoot = compiler.context;
16
+ const useExtractOptions = this.metaOptions;
17
+
18
+ // 在每次编译开始时重置脚本索引计数器,确保文件名一致性
19
+ compiler.hooks.thisCompilation.tap('ArkResetScriptIdx', () => {
20
+ resetScriptIdx();
21
+ });
22
+
23
+ compiler.hooks.thisCompilation.tap('ArkDynamicCdnChunkLoaderPlugin', (compilation) => {
24
+ compilation.hooks.processAssets.tap(
25
+ {
26
+ name: 'ArkDynamicCdnChunkLoaderPlugin',
27
+ stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
28
+ },
29
+ (assets) => {
30
+ // 新增分块文件过滤逻辑
31
+ const chunkFiles = new Set();
32
+ compilation.chunks.forEach(chunk => {
33
+ chunk.files.forEach(file => {
34
+ if (file.endsWith('.js')) chunkFiles.add(file);
35
+ });
36
+ });
37
+ // 调试日志
38
+ verbose('[分块文件列表]', Array.from(chunkFiles));
39
+
40
+ Object.entries(assets).forEach(([filename, source]) => {
41
+ // 仅处理分块JS文件
42
+ if (!chunkFiles.has(filename)) return;
43
+
44
+ // 检查资源内容有效性
45
+ if (!source || !source.source || typeof source.source() !== 'string' || source.source().trim() === '') {
46
+ console.warn(`资源 ${filename} 内容为空或无效,跳过处理`);
47
+ return;
48
+ }
49
+
50
+ let content = source.source().toString();
51
+
52
+ // 替换逻辑
53
+ // /(__webpack_require__\.l)\(url,/g)
54
+ // const webpackRequireLPattern = /(__webpack_require__\.l)\(url,/g;
55
+ // if (webpackRequireLPattern.test(content)) {
56
+ // verbose('检测到 __webpack_require__.l 模式');
57
+ // content = content.replace(
58
+ // webpackRequireLPattern,
59
+ // `(function(url, done, key, chunkId) {
60
+ // if (typeof window.ArkConfig !== 'undefined' && window.ArkConfig.cdnHost) {
61
+ // if (!url.startsWith(window.ArkConfig.cdnHost) && url.startsWith('https://unpkg.com')) {
62
+ // url = window.ArkConfig.cdnHost + url.slice('https://unpkg.com'.length);
63
+ // }
64
+ // }
65
+ // return $1(url, done, key, chunkId);
66
+ // })(url,`
67
+ // );
68
+ // const webpackRequireLPattern = /"(https:\/\/unpkg\.com)/g;
69
+ // 新:捕获前面可选的 \(eval 模式下会有 \")
70
+ const webpackRequireLPattern = /(\\?")https:\/\/unpkg\.com/g;
71
+ if (webpackRequireLPattern.test(content)) {
72
+ verbose('检测到 unpkg.com 模式');
73
+ content = content.replace(webpackRequireLPattern, `window.ArkConfig.cdnHost + "`);
74
+
75
+ // 更新资源
76
+ compilation.updateAsset(filename, new compiler.webpack.sources.RawSource(content));
77
+ }
78
+ });
79
+ }
80
+ );
81
+ });
82
+
83
+ compiler.hooks.emit.tapPromise('ArkMetaJsonGeneratorPlugin', async (compilation) => {
84
+ // 注入环境判断脚本
85
+ const isDev = process.env.NODE_ENV === 'development';
86
+
87
+ // 收集所有生成的JS文件
88
+ const outputPath = compilation.options.output.publicPath || '';
89
+ const jsFiles = [];
90
+
91
+ // Webpack 5 正确的API:使用 getAssets()
92
+ const assets = compilation.getAssets ? compilation.getAssets() : compilation.assets;
93
+
94
+ // 遍历所有资产,收集JS文件
95
+ assets.forEach((asset) => {
96
+ const assetName = asset.name || asset;
97
+ if (assetName.endsWith('.js')) {
98
+ console.log('Found JS file:', assetName);
99
+ // 排除 ark-meta.json 和 ark_userChunk 文件
100
+ if (assetName.indexOf('js/app.') !== -1 ||
101
+ assetName.indexOf('js/runtime.') !== -1 ||
102
+ assetName.indexOf('js/vendors.') !== -1 ||
103
+ assetName.indexOf('js/main.') !== -1) {
104
+ jsFiles.push(outputPath + assetName);
105
+ }
106
+ }
107
+ });
108
+
109
+ console.log('Final JS files for chunkJsSrcList:', jsFiles);
110
+
111
+ // 创建 customAssets 数组用于收集自定义资产(如从 HTML 内联 script 提取的代码)
112
+ const customAssets = [];
113
+
114
+ const arkMeta = await extractArkMetaJson({
115
+ ...useExtractOptions,
116
+ chunkJsFiles: jsFiles,
117
+ isDev: isDev,
118
+ compilation: compilation,
119
+ customAssets: customAssets
120
+ });
121
+
122
+ return Promise.resolve();
123
+ });
124
+ }
125
+ }
@@ -1,119 +1,119 @@
1
- /** @typedef {import('../../typings').ICreateSubAppOptions} ICreateSubAppOptions */
2
- import { getNpmCdnHomePage } from '../inner-utils/index';
3
-
4
- /**
5
- * @param {string} inputPath
6
- * @param {Object} options
7
- * @param {'end' | 'start'} [options.loc='end']
8
- * @param {boolean} [options.need=false]
9
- */
10
- export function ensureSlash(inputPath, options) {
11
- const { need, loc = 'end' } = options;
12
- const isEnd = loc === 'end';
13
- const hasSlash = isEnd ? inputPath.endsWith('/') : inputPath.startsWith('/');
14
-
15
- const shouldDelSlash = hasSlash && !need;
16
- const shouldAddSlash = !hasSlash && need;
17
- if (isEnd) {
18
- if (shouldDelSlash) {
19
- return inputPath.substring(0, inputPath.length - 1);
20
- }
21
- if (shouldAddSlash) {
22
- return `${inputPath}/`;
23
- }
24
- }
25
- if (!isEnd) {
26
- if (shouldDelSlash) {
27
- // del start slash
28
- return inputPath.substring(1);
29
- }
30
- if (shouldAddSlash) {
31
- return `/${inputPath}`;
32
- }
33
- }
34
-
35
- return inputPath;
36
- }
37
-
38
- /** 语义化 slash 相关操作,方便上层理解和使用 */
39
- export const slash = {
40
- start: (path) => ensureSlash(path, { loc: 'start', need: true }),
41
- noStart: (path) => ensureSlash(path, { loc: 'start', need: false }),
42
- end: (path) => ensureSlash(path, { loc: 'end', need: true }),
43
- noEnd: (path) => ensureSlash(path, { loc: 'end', need: false }),
44
- };
45
-
46
- export function getHelProcessEnvParams() {
47
- // 以下常量由蓝盾流水线注入(由流水线变量或bash脚本注入)
48
- const {
49
- HOST,
50
- PORT,
51
- // appHomePage, 形如 http://xxx.cdn.com/hel/app1_2020121201011666
52
- HEL_APP_HOME_PAGE,
53
- /** 在构建机环境时,会注入真正对应的应用名 */
54
- HEL_APP_GROUP_NAME,
55
- HEL_APP_NAME,
56
- } = process.env;
57
-
58
- let appHomePage = HEL_APP_HOME_PAGE;
59
- // 未传递 HEL_APP_HOME_PAGE 的话,根据 HOST 和 PORT 推导
60
- if (!appHomePage && (HOST || PORT)) {
61
- const host = HOST || 'localhost';
62
- const port = PORT || '80';
63
- const hotsStr = host.startsWith('http') ? host : `http://${host}`;
64
- appHomePage = `${hotsStr}:${port}`;
65
- }
66
-
67
- return {
68
- appHomePage,
69
- appGroupName: HEL_APP_GROUP_NAME,
70
- appName: HEL_APP_NAME,
71
- };
72
- }
73
-
74
- /**
75
- * @param {Record<string, any>} pkg
76
- * @param {ICreateSubAppOptions} options
77
- * @returns
78
- */
79
- export function getHelEnvParams(pkg, options = {}) {
80
- const { platform, distDir, homePage: userCustomHomePage, handleHomePage = true, npmCdnType } = options;
81
- let cdnHomePage = '';
82
- // 计算 unpkg 平台 的 homePage 值,此时如果透传了 homePage,表示 unpkg 为私服
83
- if (platform === 'unpkg' && handleHomePage) {
84
- cdnHomePage = getNpmCdnHomePage(pkg, { distDir, npmCdnType, homePage: userCustomHomePage });
85
- }
86
-
87
- // 来自 process.env 的值优先级最高
88
- const p0EnvParams = getHelProcessEnvParams();
89
- const appName = p0EnvParams.appName || pkg.appGroupName || '';
90
- return {
91
- appHomePage: p0EnvParams.appHomePage || cdnHomePage || userCustomHomePage || pkg.homepage || '/',
92
- appGroupName: p0EnvParams.appGroupName || pkg.appGroupName || appName,
93
- appName,
94
- };
95
- }
96
-
97
- /**
98
- * @param {string} appName - hel 管理台注册的应用名 或 package.name
99
- * @param {boolean} [useTimestampSuffix=true] - default: true, 是否设置时间戳后缀
100
- * 设置为 true 时,支持同一个模块正确执行多版本js文件
101
- * 如不需要同屏加载同一个模块的多个版本功能,且需要自己定制一些其他逻辑,可以设置为false
102
- */
103
- export function getJsonpFnName(appName, useTimestampSuffix = true) {
104
- if (useTimestampSuffix) {
105
- return `helJsonp_${appName}_${Date.now()}`;
106
- }
107
-
108
- return `helJsonp_${appName}`;
109
- }
110
-
111
- /**
112
- *
113
- * @param {string} homePage - 应用homePage
114
- * @param {boolean} [needSlash]
115
- * @returns
116
- */
117
- export function getPublicPathOrUrl(homePage, needSlash = true) {
118
- return ensureSlash(homePage, { loc: 'end', need: needSlash });
119
- }
1
+ /** @typedef {import('../../typings').ICreateSubAppOptions} ICreateSubAppOptions */
2
+ import { getNpmCdnHomePage } from '../inner-utils/index';
3
+
4
+ /**
5
+ * @param {string} inputPath
6
+ * @param {Object} options
7
+ * @param {'end' | 'start'} [options.loc='end']
8
+ * @param {boolean} [options.need=false]
9
+ */
10
+ export function ensureSlash(inputPath, options) {
11
+ const { need, loc = 'end' } = options;
12
+ const isEnd = loc === 'end';
13
+ const hasSlash = isEnd ? inputPath.endsWith('/') : inputPath.startsWith('/');
14
+
15
+ const shouldDelSlash = hasSlash && !need;
16
+ const shouldAddSlash = !hasSlash && need;
17
+ if (isEnd) {
18
+ if (shouldDelSlash) {
19
+ return inputPath.substring(0, inputPath.length - 1);
20
+ }
21
+ if (shouldAddSlash) {
22
+ return `${inputPath}/`;
23
+ }
24
+ }
25
+ if (!isEnd) {
26
+ if (shouldDelSlash) {
27
+ // del start slash
28
+ return inputPath.substring(1);
29
+ }
30
+ if (shouldAddSlash) {
31
+ return `/${inputPath}`;
32
+ }
33
+ }
34
+
35
+ return inputPath;
36
+ }
37
+
38
+ /** 语义化 slash 相关操作,方便上层理解和使用 */
39
+ export const slash = {
40
+ start: (path) => ensureSlash(path, { loc: 'start', need: true }),
41
+ noStart: (path) => ensureSlash(path, { loc: 'start', need: false }),
42
+ end: (path) => ensureSlash(path, { loc: 'end', need: true }),
43
+ noEnd: (path) => ensureSlash(path, { loc: 'end', need: false }),
44
+ };
45
+
46
+ export function getHelProcessEnvParams() {
47
+ // 以下常量由蓝盾流水线注入(由流水线变量或bash脚本注入)
48
+ const {
49
+ HOST,
50
+ PORT,
51
+ // appHomePage, 形如 http://xxx.cdn.com/hel/app1_2020121201011666
52
+ HEL_APP_HOME_PAGE,
53
+ /** 在构建机环境时,会注入真正对应的应用名 */
54
+ HEL_APP_GROUP_NAME,
55
+ HEL_APP_NAME,
56
+ } = process.env;
57
+
58
+ let appHomePage = HEL_APP_HOME_PAGE;
59
+ // 未传递 HEL_APP_HOME_PAGE 的话,根据 HOST 和 PORT 推导
60
+ if (!appHomePage && (HOST || PORT)) {
61
+ const host = HOST || 'localhost';
62
+ const port = PORT || '80';
63
+ const hotsStr = host.startsWith('http') ? host : `http://${host}`;
64
+ appHomePage = `${hotsStr}:${port}`;
65
+ }
66
+
67
+ return {
68
+ appHomePage,
69
+ appGroupName: HEL_APP_GROUP_NAME,
70
+ appName: HEL_APP_NAME,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * @param {Record<string, any>} pkg
76
+ * @param {ICreateSubAppOptions} options
77
+ * @returns
78
+ */
79
+ export function getHelEnvParams(pkg, options = {}) {
80
+ const { platform, distDir, homePage: userCustomHomePage, handleHomePage = true, npmCdnType } = options;
81
+ let cdnHomePage = '';
82
+ // 计算 unpkg 平台 的 homePage 值,此时如果透传了 homePage,表示 unpkg 为私服
83
+ if (platform === 'unpkg' && handleHomePage) {
84
+ cdnHomePage = getNpmCdnHomePage(pkg, { distDir, npmCdnType, homePage: userCustomHomePage });
85
+ }
86
+
87
+ // 来自 process.env 的值优先级最高
88
+ const p0EnvParams = getHelProcessEnvParams();
89
+ const appName = p0EnvParams.appName || pkg.appGroupName || '';
90
+ return {
91
+ appHomePage: p0EnvParams.appHomePage || cdnHomePage || userCustomHomePage || pkg.homepage || '/',
92
+ appGroupName: p0EnvParams.appGroupName || pkg.appGroupName || appName,
93
+ appName,
94
+ };
95
+ }
96
+
97
+ /**
98
+ * @param {string} appName - hel 管理台注册的应用名 或 package.name
99
+ * @param {boolean} [useTimestampSuffix=true] - default: true, 是否设置时间戳后缀
100
+ * 设置为 true 时,支持同一个模块正确执行多版本js文件
101
+ * 如不需要同屏加载同一个模块的多个版本功能,且需要自己定制一些其他逻辑,可以设置为false
102
+ */
103
+ export function getJsonpFnName(appName, useTimestampSuffix = true) {
104
+ if (useTimestampSuffix) {
105
+ return `helJsonp_${appName}_${Date.now()}`;
106
+ }
107
+
108
+ return `helJsonp_${appName}`;
109
+ }
110
+
111
+ /**
112
+ *
113
+ * @param {string} homePage - 应用homePage
114
+ * @param {boolean} [needSlash]
115
+ * @returns
116
+ */
117
+ export function getPublicPathOrUrl(homePage, needSlash = true) {
118
+ return ensureSlash(homePage, { loc: 'end', need: needSlash });
119
+ }