@qse/edu-scripts 0.0.0-beta.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 +5 -0
- package/README.md +107 -0
- package/app.d.ts +73 -0
- package/babel.config.json +3 -0
- package/docs/.vitepress/config.ts +35 -0
- package/docs/changelog.md +1 -0
- package/docs/debug.md +17 -0
- package/docs/deploy.md +54 -0
- package/docs/faq.md +144 -0
- package/docs/feat.md +167 -0
- package/docs/grayscale.md +31 -0
- package/docs/index.md +15 -0
- package/docs/install.md +1 -0
- package/docs/mode.md +42 -0
- package/docs/override.md +193 -0
- package/docs/refactor-react-16.md +37 -0
- package/docs/refactor.md +67 -0
- package/docs/static.md +24 -0
- package/es/asset/dll/libcommon3-manifest.json +181 -0
- package/es/asset/template/edu-app-env.d.ts.tpl +20 -0
- package/es/asset/template/edu-scripts.override.js.tpl +7 -0
- package/es/asset/template/tailwind.config.js.tpl +11 -0
- package/es/asset/template/tsconfig.json.tpl +24 -0
- package/es/auto-refactor.js +153 -0
- package/es/build.js +58 -0
- package/es/cli.js +65 -0
- package/es/commit-dist.js +83 -0
- package/es/config/paths.js +39 -0
- package/es/config/plugins/mock-server/defineMock.d.ts +6 -0
- package/es/config/plugins/mock-server/defineMock.js +7 -0
- package/es/config/plugins/mock-server/index.js +122 -0
- package/es/config/plugins/postcss-safe-area.js +22 -0
- package/es/config/plugins/ws-utils-createSocketURL.js +98 -0
- package/es/config/webpackConfig.js +418 -0
- package/es/config/webpackDevServerConfig.js +73 -0
- package/es/deploy.js +148 -0
- package/es/generator.js +52 -0
- package/es/index.d.ts +2 -0
- package/es/index.js +7 -0
- package/es/start.js +36 -0
- package/es/utils/FileSizeReporter.js +107 -0
- package/es/utils/appConfig.js +35 -0
- package/es/utils/beforeStart.js +51 -0
- package/es/utils/changeDeployVersion.js +85 -0
- package/es/utils/defineConfig.d.ts +76 -0
- package/es/utils/defineConfig.js +7 -0
- package/es/utils/exec.js +10 -0
- package/es/utils/getConfig.js +23 -0
- package/es/utils/getOverride.js +28 -0
- package/eslint.config.mjs +3 -0
- package/jest.config.mjs +199 -0
- package/package.json +95 -0
- package/src/asset/dll/libcommon3-manifest.json +181 -0
- package/src/asset/template/edu-app-env.d.ts.tpl +20 -0
- package/src/asset/template/edu-scripts.override.js.tpl +7 -0
- package/src/asset/template/tailwind.config.js.tpl +11 -0
- package/src/asset/template/tsconfig.json.tpl +24 -0
- package/src/auto-refactor.js +170 -0
- package/src/build.js +64 -0
- package/src/cli.js +88 -0
- package/src/commit-dist.js +103 -0
- package/src/config/paths.js +38 -0
- package/src/config/plugins/mock-server/defineMock.ts +12 -0
- package/src/config/plugins/mock-server/index.js +150 -0
- package/src/config/plugins/postcss-safe-area.js +21 -0
- package/src/config/plugins/ws-utils-createSocketURL.js +140 -0
- package/src/config/webpackConfig.js +444 -0
- package/src/config/webpackDevServerConfig.js +83 -0
- package/src/deploy.js +182 -0
- package/src/generator.js +67 -0
- package/src/index.ts +2 -0
- package/src/start.js +37 -0
- package/src/utils/FileSizeReporter.js +148 -0
- package/src/utils/appConfig.js +36 -0
- package/src/utils/beforeStart.js +55 -0
- package/src/utils/changeDeployVersion.js +119 -0
- package/src/utils/defineConfig.ts +81 -0
- package/src/utils/exec.js +7 -0
- package/src/utils/getConfig.js +26 -0
- package/src/utils/getOverride.js +33 -0
- package/tsconfig.json +21 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import './utils/beforeStart.js'
|
|
3
|
+
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import yargs from 'yargs'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
|
|
8
|
+
import start from './start.js'
|
|
9
|
+
import build from './build.js'
|
|
10
|
+
import deploy from './deploy.js'
|
|
11
|
+
import autoRefactor from './auto-refactor.js'
|
|
12
|
+
import commitDist from './commit-dist.js'
|
|
13
|
+
import * as generator from './generator.js'
|
|
14
|
+
|
|
15
|
+
const pkg = fs.readJsonSync(fileURLToPath(import.meta.resolve('../package.json')))
|
|
16
|
+
|
|
17
|
+
yargs(process.argv.slice(2))
|
|
18
|
+
.usage(`教育工程化 webpack5 基础框架\n文档: ${pkg.homepage}`)
|
|
19
|
+
.command(
|
|
20
|
+
'start',
|
|
21
|
+
'开发',
|
|
22
|
+
(yargs) => yargs.option('port', { alias: 'p', desc: '指定端口' }),
|
|
23
|
+
(args) => start(args)
|
|
24
|
+
)
|
|
25
|
+
.command(
|
|
26
|
+
'build',
|
|
27
|
+
'打包',
|
|
28
|
+
(yargs) =>
|
|
29
|
+
yargs
|
|
30
|
+
.option('analyze', {
|
|
31
|
+
alias: 'a',
|
|
32
|
+
desc: '分析代码',
|
|
33
|
+
default: false,
|
|
34
|
+
boolean: true,
|
|
35
|
+
})
|
|
36
|
+
.option('output-html', {
|
|
37
|
+
alias: 'o',
|
|
38
|
+
desc: '输出 html 文件',
|
|
39
|
+
default: false,
|
|
40
|
+
boolean: true,
|
|
41
|
+
}),
|
|
42
|
+
(args) => build(args)
|
|
43
|
+
)
|
|
44
|
+
.command(
|
|
45
|
+
'deploy',
|
|
46
|
+
'自动部署 dist 到 v1 服务器',
|
|
47
|
+
(yargs) =>
|
|
48
|
+
yargs
|
|
49
|
+
.option('school', { alias: 's', desc: '上传到校端', default: false, boolean: true })
|
|
50
|
+
.option('bureau', { alias: 'b', desc: '上传到局端', default: false, boolean: true })
|
|
51
|
+
.option('documentshelves', {
|
|
52
|
+
alias: 'd',
|
|
53
|
+
desc: '上传到公文',
|
|
54
|
+
default: false,
|
|
55
|
+
boolean: true,
|
|
56
|
+
})
|
|
57
|
+
.option('compositionshelves', {
|
|
58
|
+
alias: 'c',
|
|
59
|
+
desc: '上传到文曲智阅',
|
|
60
|
+
default: false,
|
|
61
|
+
boolean: true,
|
|
62
|
+
})
|
|
63
|
+
.option('compositionshelves-dingtalk', {
|
|
64
|
+
alias: 'cd',
|
|
65
|
+
desc: '上传到文曲智阅(钉钉一方化)',
|
|
66
|
+
default: false,
|
|
67
|
+
boolean: true,
|
|
68
|
+
}),
|
|
69
|
+
(args) => deploy(args)
|
|
70
|
+
)
|
|
71
|
+
.command(['generator', 'g'], '自动生成代码', (yargs) =>
|
|
72
|
+
yargs
|
|
73
|
+
.command('override', '创建 override 文件', {}, (args) => generator.override(args))
|
|
74
|
+
.command('tailwind', '创建 tailwind 文件', {}, (args) => generator.tailwind(args))
|
|
75
|
+
.command('ts', '创建 tsconfig 文件', {}, (args) => generator.ts(args))
|
|
76
|
+
.showHelpOnFail(true)
|
|
77
|
+
.demandCommand(1, '')
|
|
78
|
+
)
|
|
79
|
+
.command('auto-refactor', '自动改造项目', {}, () => autoRefactor())
|
|
80
|
+
.command(
|
|
81
|
+
'commit-dist',
|
|
82
|
+
'提交 dist 目录到 dist 分支',
|
|
83
|
+
(yargs) => yargs.option('rm-local', { desc: '提交完后删除本地 dist', type: 'boolean' }),
|
|
84
|
+
(args) => commitDist(args)
|
|
85
|
+
)
|
|
86
|
+
.showHelpOnFail(true)
|
|
87
|
+
.demandCommand(1, '')
|
|
88
|
+
.alias({ v: 'version', h: 'help' }).argv
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import paths from './config/paths.js'
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
import cp from 'child_process'
|
|
5
|
+
import tmp from 'tmp'
|
|
6
|
+
|
|
7
|
+
const exec = (cmd, opts) => cp.execSync(cmd, { encoding: 'utf-8', stdio: 'pipe', ...opts })
|
|
8
|
+
|
|
9
|
+
function validateSVNRoot(root) {
|
|
10
|
+
const ls = exec(`svn ls ${root}`)
|
|
11
|
+
return ['trunk', 'branches'].every((s) => ls.includes(s))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getWorkingCopyInfo() {
|
|
15
|
+
exec(`svn up`)
|
|
16
|
+
const url = exec(`svn info --show-item url`).trim()
|
|
17
|
+
const revision = exec(`svn info --show-item last-changed-revision`).trim()
|
|
18
|
+
const author = exec(`svn info --show-item last-changed-author`).trim()
|
|
19
|
+
|
|
20
|
+
let branch = 'trunk'
|
|
21
|
+
let root = url.replace(/\/trunk$/, '')
|
|
22
|
+
if (url.includes('/branches/')) {
|
|
23
|
+
branch = url.split('/').pop()
|
|
24
|
+
root = url.replace(/\/branches\/[^/]+$/, '')
|
|
25
|
+
}
|
|
26
|
+
let distBranchURL = root + '/branches/dist'
|
|
27
|
+
let distBranchDirURL = distBranchURL + '/' + branch
|
|
28
|
+
if (!validateSVNRoot(root)) {
|
|
29
|
+
console.log(chalk.red('SVN目录不符合规则,必须包含 trunk branches'))
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { url, branch, revision, author, distBranchURL, distBranchDirURL, root }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function copyDistToRepo(info) {
|
|
37
|
+
const tmpdir = tmp.dirSync().name
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
exec(`svn ls ${info.distBranchDirURL} --depth empty`)
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.message.includes('non-existent')) {
|
|
43
|
+
exec(
|
|
44
|
+
`svn mkdir ${info.distBranchDirURL} --parents -m "[edu-scripts] create ${info.branch} dist"`
|
|
45
|
+
)
|
|
46
|
+
} else {
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exec(`svn co ${info.distBranchDirURL} ${tmpdir}`)
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
exec(`svn rm * --force -q`, { cwd: tmpdir })
|
|
54
|
+
} catch (error) {}
|
|
55
|
+
|
|
56
|
+
fs.copySync(paths.dist, tmpdir)
|
|
57
|
+
|
|
58
|
+
exec(`svn add * --force --auto-props --parents --depth infinity -q`, { cwd: tmpdir })
|
|
59
|
+
|
|
60
|
+
const msg = `[edu-scripts] commit ${info.branch} dist #${info.revision} @${info.author}`
|
|
61
|
+
exec(`svn ci -m "${msg}"`, { cwd: tmpdir })
|
|
62
|
+
|
|
63
|
+
fs.removeSync(tmpdir)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* svn commit dist folder to dish branches
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} args
|
|
70
|
+
* @param {boolean} args.rmLocal
|
|
71
|
+
*/
|
|
72
|
+
export default async function commitDist(args) {
|
|
73
|
+
if (!fs.existsSync(paths.dist)) {
|
|
74
|
+
console.log(chalk.red('未找到 dist 文件夹,请先 edu-scpirts build'))
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (exec('svn st').trim().length) {
|
|
79
|
+
console.log(chalk.red('似乎存在未提交的代码,请提交后重试。运行 svn st 查看具体信息'))
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const info = getWorkingCopyInfo()
|
|
84
|
+
|
|
85
|
+
console.log(
|
|
86
|
+
chalk.green(
|
|
87
|
+
[
|
|
88
|
+
`分支: ${info.branch}`,
|
|
89
|
+
`版本: ${info.revision}`,
|
|
90
|
+
`作者: ${info.author}`,
|
|
91
|
+
`地址: ${info.distBranchDirURL}`,
|
|
92
|
+
].join('\n')
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
copyDistToRepo(info)
|
|
97
|
+
|
|
98
|
+
if (args.rmLocal) {
|
|
99
|
+
fs.removeSync(paths.dist)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(chalk.green('提交完成'))
|
|
103
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs-extra'
|
|
3
|
+
import { globbySync } from 'globby'
|
|
4
|
+
|
|
5
|
+
function resolveApp(...filePath) {
|
|
6
|
+
return path.resolve(process.cwd(), ...filePath)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getExistPath(...paths) {
|
|
10
|
+
for (const path of paths) {
|
|
11
|
+
if (fs.existsSync(path)) {
|
|
12
|
+
return path
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return paths[0]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const paths = {
|
|
19
|
+
resolveApp,
|
|
20
|
+
eduAppEnv: resolveApp('src', 'edu-app-env.d.ts'),
|
|
21
|
+
dist: resolveApp('dist'),
|
|
22
|
+
sshSftp: resolveApp('.sftprc.json'),
|
|
23
|
+
nodeModules: resolveApp('node_modules'),
|
|
24
|
+
tsconfig: resolveApp('tsconfig.json'),
|
|
25
|
+
jsconfig: resolveApp('jsconfig.json'),
|
|
26
|
+
package: resolveApp('package.json'),
|
|
27
|
+
tailwind: resolveApp('tailwind.config.js'),
|
|
28
|
+
indexJS: resolveApp('src', 'index.js'),
|
|
29
|
+
pages: resolveApp('src', 'pages'),
|
|
30
|
+
override: resolveApp('edu-scripts.override.js'),
|
|
31
|
+
indexHTML: globbySync('./public/*.html', { absolute: true }),
|
|
32
|
+
src: resolveApp('src'),
|
|
33
|
+
public: resolveApp('public'),
|
|
34
|
+
static: resolveApp('public', 'static'),
|
|
35
|
+
theme: getExistPath(resolveApp('theme.json'), resolveApp('theme.js')),
|
|
36
|
+
mock: resolveApp('mock'),
|
|
37
|
+
}
|
|
38
|
+
export default paths
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RequestHandler } from 'express'
|
|
2
|
+
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE'
|
|
3
|
+
type API = string
|
|
4
|
+
|
|
5
|
+
export type MockConfig = Record<
|
|
6
|
+
`${Method} ${API}`,
|
|
7
|
+
string | number | null | undefined | boolean | Record<string, any> | RequestHandler
|
|
8
|
+
>
|
|
9
|
+
|
|
10
|
+
export function defineMock(config: MockConfig) {
|
|
11
|
+
return config
|
|
12
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import paths from '../../paths.js'
|
|
2
|
+
import { globbySync } from 'globby'
|
|
3
|
+
import chokidar from 'chokidar'
|
|
4
|
+
import { debounce, memoize } from 'lodash-es'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import fs from 'fs-extra'
|
|
7
|
+
import express from 'express'
|
|
8
|
+
import cookieParser from 'cookie-parser'
|
|
9
|
+
import multer from 'multer'
|
|
10
|
+
import { pathToRegexp } from 'path-to-regexp'
|
|
11
|
+
import { createRequire } from 'node:module'
|
|
12
|
+
const require = createRequire(import.meta.url)
|
|
13
|
+
const { register } = require('@swc-node/register/register')
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @type {Record<string,{handler:any;method:string;path:string}>}
|
|
17
|
+
*/
|
|
18
|
+
let mockCache = {}
|
|
19
|
+
let isSetup = false
|
|
20
|
+
|
|
21
|
+
const setupMock = debounce(function setupMock() {
|
|
22
|
+
// clean
|
|
23
|
+
mockCache = {}
|
|
24
|
+
|
|
25
|
+
const files = globbySync(paths.mock, { expandDirectories: { extensions: ['js', 'ts'] } })
|
|
26
|
+
|
|
27
|
+
if (isSetup) {
|
|
28
|
+
console.log(chalk.green('Mock files changed, reloaded'))
|
|
29
|
+
} else {
|
|
30
|
+
isSetup = true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// setup
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
delete require.cache[require.resolve(file)]
|
|
36
|
+
try {
|
|
37
|
+
let mock = require(file)
|
|
38
|
+
mock = mock.default || mock
|
|
39
|
+
|
|
40
|
+
for (const key in mock) {
|
|
41
|
+
const [method, path] = key.split(' ')
|
|
42
|
+
mockCache[key] = { method, path, handler: mock[key] }
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error(chalk.red(`Mock file ${file} error: ${e.message}`))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, 100)
|
|
49
|
+
|
|
50
|
+
const getPathReAndKeys = memoize((path) => {
|
|
51
|
+
const keys = []
|
|
52
|
+
const re = pathToRegexp(path, keys)
|
|
53
|
+
return { re, keys }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
function decodeParam(val) {
|
|
57
|
+
if (typeof val !== 'string' || val.length === 0) {
|
|
58
|
+
return val
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return decodeURIComponent(val)
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err instanceof URIError) {
|
|
64
|
+
err.message = `Failed to decode param ' ${val} '`
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
err.status = 400
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
err.statusCode = 400
|
|
69
|
+
}
|
|
70
|
+
throw err
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {import('express').RequestHandler}
|
|
76
|
+
*/
|
|
77
|
+
function mockMiddlewave(req, res, next) {
|
|
78
|
+
const { method, path } = req
|
|
79
|
+
for (const key in mockCache) {
|
|
80
|
+
const mock = mockCache[key]
|
|
81
|
+
if (mock.method !== method) continue
|
|
82
|
+
const { keys, re } = getPathReAndKeys(mock.path)
|
|
83
|
+
const m = re.exec(path)
|
|
84
|
+
if (m) {
|
|
85
|
+
console.log(chalk.green(`Mock: ${key}`))
|
|
86
|
+
res.setHeader('X-Mock', key)
|
|
87
|
+
|
|
88
|
+
if (typeof mock.handler === 'function') {
|
|
89
|
+
const params = {}
|
|
90
|
+
for (let i = 1; i < m.length; i += 1) {
|
|
91
|
+
const key = keys[i - 1]
|
|
92
|
+
const prop = key.name
|
|
93
|
+
const val = decodeParam(m[i])
|
|
94
|
+
if (val !== undefined) {
|
|
95
|
+
params[prop] = val
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
req.params = params
|
|
99
|
+
|
|
100
|
+
const middelwaves = [
|
|
101
|
+
express.urlencoded({ limit: '5mb', extended: true }),
|
|
102
|
+
express.json({ limit: '5mb', strict: false }),
|
|
103
|
+
multer().any(),
|
|
104
|
+
cookieParser(),
|
|
105
|
+
mock.handler,
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
const throwNext = () => {
|
|
109
|
+
console.log(chalk.red(`Mock: ${key} don't use next()`))
|
|
110
|
+
res.sendStatus(500)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const mwNext = () => {
|
|
114
|
+
const middelwave = middelwaves.shift()
|
|
115
|
+
middelwave(req, res, middelwaves.length ? mwNext : throwNext)
|
|
116
|
+
}
|
|
117
|
+
mwNext()
|
|
118
|
+
} else {
|
|
119
|
+
res.json(mock.handler)
|
|
120
|
+
}
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
next()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {import('webpack-dev-server').Middleware[]} middelwaves
|
|
129
|
+
* @param {import('webpack-dev-server')} devServer
|
|
130
|
+
*/
|
|
131
|
+
function setupMockServer(middelwaves, devServer) {
|
|
132
|
+
if (!fs.existsSync(paths.mock)) return
|
|
133
|
+
|
|
134
|
+
register()
|
|
135
|
+
|
|
136
|
+
middelwaves.unshift({
|
|
137
|
+
name: 'edu-scripts-mock-middelwave',
|
|
138
|
+
middleware: mockMiddlewave,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
console.log(
|
|
142
|
+
`<i> ${chalk.green.bold('[edu-scripts] Mock server created:')} ${chalk.cyan.bold(paths.mock)}`
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
chokidar.watch(paths.mock).on('all', setupMock)
|
|
146
|
+
|
|
147
|
+
return middelwaves
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export default setupMockServer
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const vars = [
|
|
2
|
+
'safe-area-inset-top',
|
|
3
|
+
'safe-area-inset-bottom',
|
|
4
|
+
'safe-area-inset-left',
|
|
5
|
+
'safe-area-inset-right',
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
const expr = new RegExp(`env\\(\\s*(${vars.join('|')})\\s*,?\\s*([^)]+)?\\s*\\)`, 'g')
|
|
9
|
+
|
|
10
|
+
/** @type {import('postcss').PluginCreator} */
|
|
11
|
+
export default () => {
|
|
12
|
+
return {
|
|
13
|
+
postcssPlugin: 'postcss-safe-area',
|
|
14
|
+
Declaration(decl) {
|
|
15
|
+
const fallback = decl.value.replace(expr, (match, param, defaultValue) => defaultValue || '0')
|
|
16
|
+
if (fallback !== decl.value) {
|
|
17
|
+
decl.cloneBefore({ value: fallback })
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-globals */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {{ protocol?: string, auth?: string, hostname?: string, port?: string, pathname?: string, search?: string, hash?: string, slashes?: boolean }} objURL
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
function format(objURL) {
|
|
8
|
+
var protocol = objURL.protocol || ''
|
|
9
|
+
if (protocol && protocol.substr(-1) !== ':') {
|
|
10
|
+
protocol += ':'
|
|
11
|
+
}
|
|
12
|
+
var auth = objURL.auth || ''
|
|
13
|
+
if (auth) {
|
|
14
|
+
auth = encodeURIComponent(auth)
|
|
15
|
+
auth = auth.replace(/%3A/i, ':')
|
|
16
|
+
auth += '@'
|
|
17
|
+
}
|
|
18
|
+
var host = ''
|
|
19
|
+
if (objURL.hostname) {
|
|
20
|
+
host =
|
|
21
|
+
auth +
|
|
22
|
+
(objURL.hostname.indexOf(':') === -1 ? objURL.hostname : '['.concat(objURL.hostname, ']'))
|
|
23
|
+
if (objURL.port) {
|
|
24
|
+
host += ':'.concat(objURL.port)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var pathname = objURL.pathname || ''
|
|
28
|
+
if (objURL.slashes) {
|
|
29
|
+
host = '//'.concat(host || '')
|
|
30
|
+
if (pathname && pathname.charAt(0) !== '/') {
|
|
31
|
+
pathname = '/'.concat(pathname)
|
|
32
|
+
}
|
|
33
|
+
} else if (!host) {
|
|
34
|
+
host = ''
|
|
35
|
+
}
|
|
36
|
+
var search = objURL.search || ''
|
|
37
|
+
if (search && search.charAt(0) !== '?') {
|
|
38
|
+
search = '?'.concat(search)
|
|
39
|
+
}
|
|
40
|
+
var hash = objURL.hash || ''
|
|
41
|
+
if (hash && hash.charAt(0) !== '#') {
|
|
42
|
+
hash = '#'.concat(hash)
|
|
43
|
+
}
|
|
44
|
+
pathname = pathname.replace(
|
|
45
|
+
/[?#]/g,
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} match
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
function (match) {
|
|
51
|
+
return encodeURIComponent(match)
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
search = search.replace('#', '%23')
|
|
55
|
+
return ''.concat(protocol).concat(host).concat(pathname).concat(search).concat(hash)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {URL & { fromCurrentScript?: boolean }} parsedURL
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
function createSocketURL(parsedURL) {
|
|
63
|
+
var hostname = parsedURL.hostname
|
|
64
|
+
|
|
65
|
+
// Node.js module parses it as `::`
|
|
66
|
+
// `new URL(urlString, [baseURLString])` parses it as '[::]'
|
|
67
|
+
var isInAddrAny = hostname === '0.0.0.0' || hostname === '::' || hostname === '[::]'
|
|
68
|
+
|
|
69
|
+
// why do we need this check?
|
|
70
|
+
// hostname n/a for file protocol (example, when using electron, ionic)
|
|
71
|
+
// see: https://github.com/webpack/webpack-dev-server/pull/384
|
|
72
|
+
if (isInAddrAny && self.location.hostname && self.location.protocol.indexOf('http') === 0) {
|
|
73
|
+
hostname = self.location.hostname
|
|
74
|
+
}
|
|
75
|
+
var socketURLProtocol = parsedURL.protocol || self.location.protocol
|
|
76
|
+
|
|
77
|
+
// When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets.
|
|
78
|
+
if (
|
|
79
|
+
socketURLProtocol === 'auto:' ||
|
|
80
|
+
(hostname && isInAddrAny && self.location.protocol === 'https:')
|
|
81
|
+
) {
|
|
82
|
+
socketURLProtocol = self.location.protocol
|
|
83
|
+
}
|
|
84
|
+
socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, 'ws')
|
|
85
|
+
var socketURLAuth = ''
|
|
86
|
+
|
|
87
|
+
// `new URL(urlString, [baseURLstring])` doesn't have `auth` property
|
|
88
|
+
// Parse authentication credentials in case we need them
|
|
89
|
+
if (parsedURL.username) {
|
|
90
|
+
socketURLAuth = parsedURL.username
|
|
91
|
+
|
|
92
|
+
// Since HTTP basic authentication does not allow empty username,
|
|
93
|
+
// we only include password if the username is not empty.
|
|
94
|
+
if (parsedURL.password) {
|
|
95
|
+
// Result: <username>:<password>
|
|
96
|
+
socketURLAuth = socketURLAuth.concat(':', parsedURL.password)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// In case the host is a raw IPv6 address, it can be enclosed in
|
|
101
|
+
// the brackets as the brackets are needed in the final URL string.
|
|
102
|
+
// Need to remove those as url.format blindly adds its own set of brackets
|
|
103
|
+
// if the host string contains colons. That would lead to non-working
|
|
104
|
+
// double brackets (e.g. [[::]]) host
|
|
105
|
+
//
|
|
106
|
+
// All of these web socket url params are optionally passed in through resourceQuery,
|
|
107
|
+
// so we need to fall back to the default if they are not provided
|
|
108
|
+
var socketURLHostname = (hostname || self.location.hostname || 'localhost').replace(
|
|
109
|
+
/^\[(.*)\]$/,
|
|
110
|
+
'$1'
|
|
111
|
+
)
|
|
112
|
+
var socketURLPort = parsedURL.port
|
|
113
|
+
if (!socketURLPort || socketURLPort === '0') {
|
|
114
|
+
socketURLPort = self.location.port
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If path is provided it'll be passed in via the resourceQuery as a
|
|
118
|
+
// query param so it has to be parsed out of the querystring in order for the
|
|
119
|
+
// client to open the socket to the correct location.
|
|
120
|
+
var socketURLPathname = '/ws'
|
|
121
|
+
if (parsedURL.pathname && !parsedURL.fromCurrentScript) {
|
|
122
|
+
socketURLPathname = parsedURL.pathname
|
|
123
|
+
|
|
124
|
+
if (hostname.indexOf('zhidianbao.cn') > -1 || hostname.indexOf('qsban.cn') > -1) {
|
|
125
|
+
var ctx = self.location.pathname.split('/')[1]
|
|
126
|
+
if (ctx) {
|
|
127
|
+
socketURLPathname = '/' + ctx + socketURLPathname
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return format({
|
|
132
|
+
protocol: socketURLProtocol,
|
|
133
|
+
auth: socketURLAuth,
|
|
134
|
+
hostname: socketURLHostname,
|
|
135
|
+
port: socketURLPort,
|
|
136
|
+
pathname: socketURLPathname,
|
|
137
|
+
slashes: true,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
export default createSocketURL
|