zx-bulk-release 1.26.0 → 1.26.2
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 +10 -0
- package/README.md +3 -1
- package/package.json +6 -6
- package/src/main/js/analyze.js +9 -8
- package/src/main/js/build.js +1 -1
- package/src/main/js/index.js +1 -62
- package/src/main/js/log.js +9 -0
- package/src/main/js/npm.js +1 -1
- package/src/main/js/processor.js +74 -0
- package/src/main/js/publish.js +4 -2
- package/src/main/js/repo.js +3 -3
- package/src/main/js/state.js +56 -0
- package/src/main/js/tag.js +1 -1
- package/src/main/js/util.js +0 -69
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [1.26.2](https://github.com/semrel-extra/zx-bulk-release/compare/v1.26.1...v1.26.2) (2023-03-21)
|
|
2
|
+
|
|
3
|
+
### Fixes & improvements
|
|
4
|
+
* refactor: separate processor layer ([dae6b96](https://github.com/semrel-extra/zx-bulk-release/commit/dae6b9682cd7d283e4f776c69f30c5f50fe9fa41))
|
|
5
|
+
|
|
6
|
+
## [1.26.1](https://github.com/semrel-extra/zx-bulk-release/compare/v1.26.0...v1.26.1) (2023-02-08)
|
|
7
|
+
|
|
8
|
+
### Fixes & improvements
|
|
9
|
+
* fix: allow multiple scopes in prefix ([2df6cb7](https://github.com/semrel-extra/zx-bulk-release/commit/2df6cb7999bed91968693069b80adeeb29b3890a))
|
|
10
|
+
|
|
1
11
|
## [1.26.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.25.3...v1.26.0) (2023-01-17)
|
|
2
12
|
|
|
3
13
|
### Features
|
package/README.md
CHANGED
|
@@ -278,7 +278,7 @@ Release process state is reported to the console and to a file if `--report` fla
|
|
|
278
278
|
events: [
|
|
279
279
|
{msg: ['zx-bulk-release'], scope:'~', date: 1665839585488, level: 'info'},
|
|
280
280
|
{msg: ['queue:',['a','b']], scope:'~', date: 1665839585493, level: 'info'},
|
|
281
|
-
{msg: ["run buildCmd 'yarn && yarn build && yarn test'"], scope: 'a', date:1665839585719, level:'info'},
|
|
281
|
+
{msg: ["run buildCmd 'yarn && yarn build && yarn test'"], scope: 'a', date: 1665839585719, level:'info'},
|
|
282
282
|
// ...
|
|
283
283
|
]
|
|
284
284
|
}
|
|
@@ -299,6 +299,8 @@ Release process state is reported to the console and to a file if `--report` fla
|
|
|
299
299
|
* [vercel/turborepo](https://github.com/vercel/turborepo)
|
|
300
300
|
* [lerna/lerna](https://github.com/lerna/lerna)
|
|
301
301
|
* [nrwl/nx](https://github.com/nrwl/nx)
|
|
302
|
+
* [moonrepo/moon](https://github.com/moonrepo/moon)
|
|
303
|
+
* [ojkelly/yarn.build](https://github.com/ojkelly/yarn.build)
|
|
302
304
|
|
|
303
305
|
## License
|
|
304
306
|
[MIT](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zx-bulk-release",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.2",
|
|
4
4
|
"description": "zx-based alternative for multi-semantic-release",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"docs": "mkdir -p docs && cp ./README.md ./docs/README.md"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@semrel-extra/topo": "^1.
|
|
25
|
-
"cosmiconfig": "^8.
|
|
26
|
-
"zx-extra": "^2.5.
|
|
24
|
+
"@semrel-extra/topo": "^1.6.0",
|
|
25
|
+
"cosmiconfig": "^8.1.3",
|
|
26
|
+
"zx-extra": "^2.5.4"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"c8": "^7.
|
|
29
|
+
"c8": "^7.13.0",
|
|
30
30
|
"uvu": "^0.5.6",
|
|
31
|
-
"verdaccio": "^5.
|
|
31
|
+
"verdaccio": "^5.22.1"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
package/src/main/js/analyze.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ctx, semver} from 'zx-extra'
|
|
2
2
|
import {updateDeps} from './deps.js'
|
|
3
3
|
import {formatTag} from './tag.js';
|
|
4
|
-
import {log} from './
|
|
4
|
+
import {log} from './log.js'
|
|
5
5
|
|
|
6
6
|
export const analyze = async (pkg, packages) => {
|
|
7
7
|
const semanticChanges = await getSemanticChanges(pkg.absPath, pkg.latest.tag?.ref)
|
|
@@ -21,7 +21,7 @@ export const analyze = async (pkg, packages) => {
|
|
|
21
21
|
export const releaseSeverityOrder = ['major', 'minor', 'patch']
|
|
22
22
|
export const semanticRules = [
|
|
23
23
|
{group: 'Features', releaseType: 'minor', prefixes: ['feat']},
|
|
24
|
-
{group: 'Fixes & improvements', releaseType: 'patch', prefixes: ['fix', 'perf', 'refactor', 'docs']},
|
|
24
|
+
{group: 'Fixes & improvements', releaseType: 'patch', prefixes: ['fix', 'perf', 'refactor', 'docs', 'patch']},
|
|
25
25
|
{group: 'BREAKING CHANGES', releaseType: 'major', keywords: ['BREAKING CHANGE', 'BREAKING CHANGES']},
|
|
26
26
|
]
|
|
27
27
|
|
|
@@ -41,10 +41,14 @@ export const getPkgCommits = async (cwd, since) => ctx(async ($) => {
|
|
|
41
41
|
|
|
42
42
|
export const getSemanticChanges = async (cwd, since) => {
|
|
43
43
|
const commits = await getPkgCommits(cwd, since)
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
|
|
45
|
+
return analyzeCommits(commits)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const analyzeCommits = (commits) =>
|
|
49
|
+
commits.reduce((acc, {subj, body, short, hash}) => {
|
|
46
50
|
semanticRules.forEach(({group, releaseType, prefixes, keywords}) => {
|
|
47
|
-
const prefixMatcher = prefixes && new RegExp(`^(${prefixes.join('|')})(\\([a-z0-9\\-_]+\\))?:\\s.+$`)
|
|
51
|
+
const prefixMatcher = prefixes && new RegExp(`^(${prefixes.join('|')})(\\([a-z0-9\\-_,]+\\))?:\\s.+$`)
|
|
48
52
|
const keywordsMatcher = keywords && new RegExp(`(${keywords.join('|')}):\\s(.+)`)
|
|
49
53
|
const change = subj.match(prefixMatcher)?.[0] || body.match(keywordsMatcher)?.[2]
|
|
50
54
|
|
|
@@ -63,9 +67,6 @@ export const getSemanticChanges = async (cwd, since) => {
|
|
|
63
67
|
return acc
|
|
64
68
|
}, [])
|
|
65
69
|
|
|
66
|
-
return semanticChanges
|
|
67
|
-
}
|
|
68
|
-
|
|
69
70
|
export const getNextReleaseType = (changes) => changes.length
|
|
70
71
|
? releaseSeverityOrder.find(type => changes.find(({releaseType}) => type === releaseType))
|
|
71
72
|
: null
|
package/src/main/js/build.js
CHANGED
package/src/main/js/index.js
CHANGED
|
@@ -1,62 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import {analyze} from './analyze.js'
|
|
3
|
-
import {publish} from './publish.js'
|
|
4
|
-
import {build} from './build.js'
|
|
5
|
-
import {contextify} from './contextify.js'
|
|
6
|
-
import {topo} from './topo.js'
|
|
7
|
-
import {createReporter, log} from './util.js';
|
|
8
|
-
|
|
9
|
-
export {getLatestTaggedVersion} from './tag.js'
|
|
10
|
-
|
|
11
|
-
export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within(async () => {
|
|
12
|
-
const reporter = $.r = createReporter(flags.report)
|
|
13
|
-
$.env = {...process.env, ...env}
|
|
14
|
-
$.verbose = !!(flags.debug || $.env.DEBUG ) || $.verbose
|
|
15
|
-
log()('zx-bulk-release')
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const {packages, queue, root} = await topo({cwd, flags})
|
|
19
|
-
log()('queue:', queue)
|
|
20
|
-
|
|
21
|
-
reporter.setQueue(queue, packages)
|
|
22
|
-
reporter.setStatus('pending')
|
|
23
|
-
|
|
24
|
-
for (let name of queue) {
|
|
25
|
-
const pkg = packages[name]
|
|
26
|
-
|
|
27
|
-
reporter.setStatus('analyzing', name)
|
|
28
|
-
await contextify(pkg, packages, root)
|
|
29
|
-
await analyze(pkg, packages)
|
|
30
|
-
reporter.setState('config', pkg.config, name)
|
|
31
|
-
reporter.setState('version', pkg.version, name)
|
|
32
|
-
reporter.setState('prevVersion', pkg.latest.tag?.version || pkg.manifest.version, name)
|
|
33
|
-
reporter.setState('releaseType', pkg.releaseType, name)
|
|
34
|
-
reporter.setState('tag', pkg.tag, name)
|
|
35
|
-
|
|
36
|
-
if (!pkg.releaseType) {
|
|
37
|
-
reporter.setStatus('skipped', name)
|
|
38
|
-
continue
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
reporter.setStatus('building', name)
|
|
42
|
-
await build(pkg, packages)
|
|
43
|
-
|
|
44
|
-
if (flags.dryRun) {
|
|
45
|
-
reporter.setStatus('success', name)
|
|
46
|
-
continue
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
reporter.setStatus('publishing', name)
|
|
50
|
-
await publish(pkg)
|
|
51
|
-
|
|
52
|
-
reporter.setStatus('success', name)
|
|
53
|
-
}
|
|
54
|
-
} catch (e) {
|
|
55
|
-
log({level: 'error'})(e)
|
|
56
|
-
reporter.setState('error', e)
|
|
57
|
-
reporter.setStatus('failure')
|
|
58
|
-
throw e
|
|
59
|
-
}
|
|
60
|
-
reporter.setStatus('success')
|
|
61
|
-
log()('Great success!')
|
|
62
|
-
})
|
|
1
|
+
export {run} from './processor.js'
|
package/src/main/js/npm.js
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import os from 'node:os'
|
|
2
|
+
import {$, fs, within} from 'zx-extra'
|
|
3
|
+
import {log} from './log.js'
|
|
4
|
+
import {topo} from './topo.js'
|
|
5
|
+
import {contextify} from './contextify.js'
|
|
6
|
+
import {analyze} from './analyze.js'
|
|
7
|
+
import {build} from './build.js'
|
|
8
|
+
import {publish} from './publish.js'
|
|
9
|
+
import {createState} from './state.js'
|
|
10
|
+
import {tpl} from "./util.js";
|
|
11
|
+
|
|
12
|
+
export const run = async ({cwd = process.cwd(), env, flags = {}, concurrency = os.cpus().length} = {}) => within(async () => {
|
|
13
|
+
const state = createState({file: flags.report})
|
|
14
|
+
$.state = state
|
|
15
|
+
$.env = {...process.env, ...env}
|
|
16
|
+
$.verbose = !!(flags.debug || $.env.DEBUG ) || $.verbose
|
|
17
|
+
|
|
18
|
+
log()('zx-bulk-release')
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const {packages, queue, root} = await topo({cwd, flags})
|
|
22
|
+
log()('queue:', queue)
|
|
23
|
+
|
|
24
|
+
state.setQueue(queue, packages)
|
|
25
|
+
state.setStatus('pending')
|
|
26
|
+
|
|
27
|
+
for (let name of queue) {
|
|
28
|
+
const pkg = packages[name]
|
|
29
|
+
|
|
30
|
+
state.setStatus('analyzing', name)
|
|
31
|
+
await contextify(pkg, packages, root)
|
|
32
|
+
await analyze(pkg, packages)
|
|
33
|
+
state.set('config', pkg.config, name)
|
|
34
|
+
state.set('version', pkg.version, name)
|
|
35
|
+
state.set('prevVersion', pkg.latest.tag?.version || pkg.manifest.version, name)
|
|
36
|
+
state.set('releaseType', pkg.releaseType, name)
|
|
37
|
+
state.set('tag', pkg.tag, name)
|
|
38
|
+
|
|
39
|
+
if (!pkg.releaseType) {
|
|
40
|
+
state.setStatus('skipped', name)
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
state.setStatus('building', name)
|
|
45
|
+
await build(pkg, packages)
|
|
46
|
+
|
|
47
|
+
if (flags.dryRun) {
|
|
48
|
+
state.setStatus('success', name)
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
state.setStatus('publishing', name)
|
|
53
|
+
await publish(pkg)
|
|
54
|
+
|
|
55
|
+
state.setStatus('success', name)
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
log({level: 'error'})(e)
|
|
59
|
+
state.set('error', e)
|
|
60
|
+
state.setStatus('failure')
|
|
61
|
+
throw e
|
|
62
|
+
}
|
|
63
|
+
state.setStatus('success')
|
|
64
|
+
log()('Great success!')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
export const runHook = async (pkg, name) => {
|
|
68
|
+
const cmd = tpl(pkg.config[name], {...pkg, ...pkg.context})
|
|
69
|
+
|
|
70
|
+
if (cmd) {
|
|
71
|
+
log({pkg})(`run ${name} '${cmd}'`)
|
|
72
|
+
await $.o({cwd: pkg.absPath, quote: v => v, preferLocal: true})`${cmd}`
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/main/js/publish.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {formatTag, getLatestTag, pushTag} from './tag.js'
|
|
2
1
|
import {fs, path, $} from 'zx-extra'
|
|
2
|
+
import {formatTag, getLatestTag, pushTag} from './tag.js'
|
|
3
3
|
import {push, fetch, parseRepo} from './repo.js'
|
|
4
4
|
import {parseEnv} from './config.js'
|
|
5
5
|
import {fetchManifest, npmPublish} from './npm.js'
|
|
6
|
-
import {restJoin
|
|
6
|
+
import {restJoin} from './util.js'
|
|
7
|
+
import {runHook} from './processor.js'
|
|
8
|
+
import {log} from './log.js'
|
|
7
9
|
|
|
8
10
|
export const publish = async (pkg) => {
|
|
9
11
|
await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
|
package/src/main/js/repo.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {$, ctx, fs, path, tempy, copy} from 'zx-extra'
|
|
2
2
|
import {parseEnv} from './config.js'
|
|
3
|
-
import {log} from './
|
|
3
|
+
import {log} from './log.js'
|
|
4
4
|
|
|
5
5
|
const branches = {}
|
|
6
6
|
export const fetch = async ({cwd: _cwd, branch, origin: _origin}) => ctx(async ($) => {
|
|
@@ -58,8 +58,8 @@ export const parseRepo = async (_cwd) => {
|
|
|
58
58
|
const originUrl = await getOrigin(cwd)
|
|
59
59
|
const [, , repoHost, repoName] = originUrl.replace(':', '/').replace(/\.git/, '').match(/.+(@|\/\/)([^/]+)\/(.+)$/) || []
|
|
60
60
|
const repoPublicUrl = `https://${repoHost}/${repoName}`
|
|
61
|
-
const repoAuthedUrl = ghToken && ghUser && repoHost && repoName
|
|
62
|
-
`https://${ghUser}:${ghToken}@${repoHost}/${repoName}.git`
|
|
61
|
+
const repoAuthedUrl = ghToken && ghUser && repoHost && repoName
|
|
62
|
+
? `https://${ghUser}:${ghToken}@${repoHost}/${repoName}.git`
|
|
63
63
|
: originUrl
|
|
64
64
|
|
|
65
65
|
repos[cwd] = {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {get, set, tpl} from './util.js'
|
|
2
|
+
import {fs} from 'zx-extra'
|
|
3
|
+
|
|
4
|
+
export const createState = ({logger = console, file = null} = {}) => ({
|
|
5
|
+
logger,
|
|
6
|
+
file,
|
|
7
|
+
status: 'initial',
|
|
8
|
+
queue: [],
|
|
9
|
+
packages: [],
|
|
10
|
+
events: [],
|
|
11
|
+
setQueue(queue, packages) {
|
|
12
|
+
this.queue = queue
|
|
13
|
+
this.packages = queue.map(name => {
|
|
14
|
+
const {manifest: {version}, absPath, relPath} = packages[name]
|
|
15
|
+
return {
|
|
16
|
+
status: 'initial',
|
|
17
|
+
name,
|
|
18
|
+
version,
|
|
19
|
+
path: absPath,
|
|
20
|
+
relPath
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
},
|
|
24
|
+
get(key, pkgName) {
|
|
25
|
+
return get(
|
|
26
|
+
pkgName ? this.packages.find(({name}) => name === pkgName) : this,
|
|
27
|
+
key
|
|
28
|
+
)
|
|
29
|
+
},
|
|
30
|
+
set(key, value, pkgName) {
|
|
31
|
+
set(
|
|
32
|
+
pkgName ? this.packages.find(({name}) => name === pkgName) : this,
|
|
33
|
+
key,
|
|
34
|
+
value
|
|
35
|
+
)
|
|
36
|
+
},
|
|
37
|
+
setStatus(status, name) {
|
|
38
|
+
this.set('status', status, name)
|
|
39
|
+
this.save()
|
|
40
|
+
},
|
|
41
|
+
getStatus(status, name) {
|
|
42
|
+
return this.get('status', name)
|
|
43
|
+
},
|
|
44
|
+
log(ctx = {}) {
|
|
45
|
+
return function (...chunks) {
|
|
46
|
+
const {pkg, scope = pkg?.name || '~', level = 'info'} = ctx
|
|
47
|
+
const msg = chunks.map(c => typeof c === 'string' ? tpl(c, ctx) : c)
|
|
48
|
+
const event = {msg, scope, date: Date.now(), level}
|
|
49
|
+
this.events.push(event)
|
|
50
|
+
logger[level](`[${scope}]`, ...msg)
|
|
51
|
+
}.bind(this)
|
|
52
|
+
},
|
|
53
|
+
save() {
|
|
54
|
+
this.file && fs.outputJsonSync(this.file, this)
|
|
55
|
+
}
|
|
56
|
+
})
|
package/src/main/js/tag.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import {ctx, semver, $} from 'zx-extra'
|
|
4
4
|
import {Buffer} from 'buffer'
|
|
5
5
|
import {parseEnv} from './config.js'
|
|
6
|
-
import {log} from './
|
|
6
|
+
import {log} from './log.js'
|
|
7
7
|
|
|
8
8
|
export const pushTag = (pkg) => ctx(async ($) => {
|
|
9
9
|
const {absPath: cwd, name, version} = pkg
|
package/src/main/js/util.js
CHANGED
|
@@ -30,73 +30,4 @@ export const set = (obj, path, value) => {
|
|
|
30
30
|
return result
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export const runHook = async (pkg, name) => {
|
|
34
|
-
const cmd = tpl(pkg.config[name], {...pkg, ...pkg.context})
|
|
35
|
-
|
|
36
|
-
if (cmd) {
|
|
37
|
-
log({pkg})(`run ${name} '${cmd}'`)
|
|
38
|
-
await $.o({cwd: pkg.absPath, quote: v => v, preferLocal: true})`${cmd}`
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
33
|
export const restJoin = (rest, context, def) => tpl(rest.filter(Boolean).join(' ') || def, context)
|
|
43
|
-
|
|
44
|
-
export const createReporter = (file, logger = console) => {
|
|
45
|
-
const state = {
|
|
46
|
-
status: 'initial',
|
|
47
|
-
queue: [],
|
|
48
|
-
packages: [],
|
|
49
|
-
events: [],
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
setQueue(queue, packages) {
|
|
54
|
-
state.queue = queue
|
|
55
|
-
state.packages = queue.map(name => {
|
|
56
|
-
const {manifest: {version}, absPath, relPath} = packages[name]
|
|
57
|
-
return {
|
|
58
|
-
status: 'initial',
|
|
59
|
-
name,
|
|
60
|
-
version,
|
|
61
|
-
path: absPath,
|
|
62
|
-
relPath
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
},
|
|
66
|
-
getState(key, pkgName) {
|
|
67
|
-
const _state = pkgName ? state.packages.find(({name}) => name === pkgName) : state
|
|
68
|
-
return get(_state, key)
|
|
69
|
-
},
|
|
70
|
-
setState(key, value, pkgName) {
|
|
71
|
-
const _state = pkgName ? state.packages.find(({name}) => name === pkgName) : state
|
|
72
|
-
set(_state, key, value)
|
|
73
|
-
},
|
|
74
|
-
setStatus(status, name) {
|
|
75
|
-
this.setState('status', status, name)
|
|
76
|
-
|
|
77
|
-
this.persistState()
|
|
78
|
-
},
|
|
79
|
-
getStatus (status, name) {
|
|
80
|
-
return this.getState('status', name)
|
|
81
|
-
},
|
|
82
|
-
log(ctx = {}) { return (...chunks) => {
|
|
83
|
-
const {pkg, scope = pkg?.name || '~', level = 'info'} = ctx
|
|
84
|
-
const msg = chunks.map(c => typeof c === 'string' ? tpl(c, ctx) : c)
|
|
85
|
-
const event = {msg, scope, date: Date.now(), level}
|
|
86
|
-
state.events.push(event)
|
|
87
|
-
logger[level](`[${scope}]`, ...msg)
|
|
88
|
-
}},
|
|
89
|
-
persistState() {
|
|
90
|
-
file && fs.outputJsonSync(file, state)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const log = (ctx) => {
|
|
96
|
-
if (!$.r) {
|
|
97
|
-
$.r = createReporter()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return $.r.log(ctx)
|
|
101
|
-
}
|
|
102
|
-
|