redlint 1.0.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/LICENSE +21 -0
- package/README.md +326 -0
- package/bin/redlint.js +8 -0
- package/lib/redlint.js +58 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) coderaiser
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Printer [![License][LicenseIMGURL]][LicenseURL] [![NPM version][NPMIMGURL]][NPMURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Coverage Status][CoverageIMGURL]][CoverageURL]
|
|
2
|
+
|
|
3
|
+
[NPMURL]: https://npmjs.org/package/@putout/printer "npm"
|
|
4
|
+
[NPMIMGURL]: https://img.shields.io/npm/v/@putout/printer.svg?style=flat&longCache=true
|
|
5
|
+
[BuildStatusURL]: https://github.com/putoutjs/printer/actions/workflows/nodejs.yml "Build Status"
|
|
6
|
+
[BuildStatusIMGURL]: https://github.com/putoutjs/printer/actions/workflows/nodejs.yml/badge.svg
|
|
7
|
+
[LicenseURL]: https://tldrlegal.com/license/mit-license "MIT License"
|
|
8
|
+
[LicenseIMGURL]: https://img.shields.io/badge/license-MIT-317BF9.svg?style=flat
|
|
9
|
+
[CoverageURL]: https://coveralls.io/github/putoutjs/printer?branch=master
|
|
10
|
+
[CoverageIMGURL]: https://coveralls.io/repos/putoutjs/printer/badge.svg?branch=master&service=github
|
|
11
|
+
|
|
12
|
+
Prints [**Babel AST**](https://github.com/coderaiser/estree-to-babel) to readable **JavaScript**. For **ESTree** use [`estree-to-babel`](https://github.com/coderaiser/estree-to-babel).
|
|
13
|
+
|
|
14
|
+
- ☝️ Similar to **Recast**, but [twice faster](#speed-comparison), also simpler and easier in maintenance, since it supports only **Babel**.
|
|
15
|
+
- ☝️ As opinionated as **Prettier**, but has more user-friendly output and works directly with **AST**.
|
|
16
|
+
- ☝️ Like **ESLint** but works directly with **Babel AST**.
|
|
17
|
+
- ☝️ Easily extendable with help of [Overrides](#overrides).
|
|
18
|
+
|
|
19
|
+
Supports:
|
|
20
|
+
|
|
21
|
+
- ✅ **ES2023**;
|
|
22
|
+
- ✅ **JSX**;
|
|
23
|
+
- ✅ **TypeScript**;
|
|
24
|
+
- ✅ [**JSON**](https://github.com/coderaiser/putout/tree/master/packages/processor-json#readme);
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
npm i @putout/printer
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 🐊 Support of Printer
|
|
33
|
+
|
|
34
|
+
**Printer** has first class support from 🐊**Putout** with help of [`@putout/plugin-printer`](https://github.com/coderaiser/putout/tree/master/packages/plugin-printer#putoutplugin-printer-). So install:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npm i @putout/plugin-printer -aD
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
And update `.putout.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"printer": "putout",
|
|
45
|
+
"plugins": ["printer"]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
To benefit from it.
|
|
50
|
+
|
|
51
|
+
## Example
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const {print} = require('@putout/printer');
|
|
55
|
+
const {parse} = require('@babel/parser');
|
|
56
|
+
const ast = parse('const a = (b, c) => {const d = 5; return a;}');
|
|
57
|
+
|
|
58
|
+
print(ast);
|
|
59
|
+
// returns
|
|
60
|
+
`
|
|
61
|
+
const a = (b, c) => {
|
|
62
|
+
const d = 5;
|
|
63
|
+
return a;
|
|
64
|
+
};
|
|
65
|
+
`;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Overrides
|
|
69
|
+
|
|
70
|
+
When you need to extend syntax of `@putout/printer` just pass a function which receives:
|
|
71
|
+
|
|
72
|
+
- `path`, Babel Path
|
|
73
|
+
- `write`, a function to output result of printing into token array;
|
|
74
|
+
|
|
75
|
+
When `path` contains to dashes `__` and name, it is the same as: `write(path.get('right'))`, and this is
|
|
76
|
+
actually `traverse(path.get('right'))` shortened to simplify read and process.
|
|
77
|
+
|
|
78
|
+
Here is how you can override `AssignmentPattern`:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
const ast = parse('const {a = 5} = b');
|
|
82
|
+
|
|
83
|
+
print(ast, {
|
|
84
|
+
format: {
|
|
85
|
+
indent: ' ',
|
|
86
|
+
newline: '\n',
|
|
87
|
+
space: ' ',
|
|
88
|
+
splitter: '\n',
|
|
89
|
+
roundBraceOpen: '(',
|
|
90
|
+
roundBraceClose: ')',
|
|
91
|
+
quote: `'`,
|
|
92
|
+
endOfFile: '\n',
|
|
93
|
+
},
|
|
94
|
+
semantics: {
|
|
95
|
+
comments: true,
|
|
96
|
+
maxSpecifiersInOneLine: 2,
|
|
97
|
+
maxElementsInOneLine: 3,
|
|
98
|
+
maxVariablesInOneLine: 4,
|
|
99
|
+
maxPropertiesInOneLine: 2,
|
|
100
|
+
trailingComma: true,
|
|
101
|
+
},
|
|
102
|
+
visitors: {
|
|
103
|
+
AssignmentPattern(path, {print}) {
|
|
104
|
+
print('/* [hello world] */= ');
|
|
105
|
+
print('__right');
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// returns
|
|
111
|
+
'const {a/* [hello world] */= 5} = b;\n';
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `format`
|
|
115
|
+
|
|
116
|
+
Options related to visuals and not related to logic of output can be changed with help of `format`,
|
|
117
|
+
you can override next options:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
const overrides = {
|
|
121
|
+
format: {
|
|
122
|
+
indent: ' ',
|
|
123
|
+
newline: '\n',
|
|
124
|
+
space: ' ',
|
|
125
|
+
splitter: '\n',
|
|
126
|
+
roundBraceOpen: '(',
|
|
127
|
+
roundBraceClose: ')',
|
|
128
|
+
endOfFile: '\n',
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- `indent` - use two spaces, tabs, or anything you want;
|
|
134
|
+
- `newline` - symbol for used for line separation;
|
|
135
|
+
- `space` - default symbol used for space character;
|
|
136
|
+
- `splitter` - mandatory symbol that used inside of statements like this:
|
|
137
|
+
- `roundBraceOpen` and `roundBraceClose` symbols to output braces in a single argument arrow function expressions: `(a) => {}`.
|
|
138
|
+
|
|
139
|
+
Default options produce:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
if (a > 3)
|
|
143
|
+
console.log('ok');
|
|
144
|
+
else
|
|
145
|
+
console.log('not ok');
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
But you can override them with:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
const overrides = {
|
|
152
|
+
format: {
|
|
153
|
+
indent: '',
|
|
154
|
+
newline: '',
|
|
155
|
+
space: '',
|
|
156
|
+
splitter: ' ',
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
And have minified code:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
if(a>3)console.log('ok');else console.log('not ok');
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Semantics
|
|
168
|
+
|
|
169
|
+
Options used to configure logic of output, similar to ESLint rules:
|
|
170
|
+
|
|
171
|
+
- ✅ `maxElementsInOneLine` - count of `ArrayExpression` and `ArrayPattern` elements placed in one line.
|
|
172
|
+
- ✅ `maxVariablesInOneLine` - count of `VariableDeclarators` in one line.
|
|
173
|
+
- ✅ `maxPropertiesInOneLine` - count of `ObjectProperties` in one line.
|
|
174
|
+
|
|
175
|
+
## Visitors API
|
|
176
|
+
|
|
177
|
+
When you want to improve support of existing visitor or extend **Printer** with a new ones, you need next base operations:
|
|
178
|
+
|
|
179
|
+
### override
|
|
180
|
+
|
|
181
|
+
When you need to override behavior of existing visitor use:
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
import {
|
|
185
|
+
print,
|
|
186
|
+
visitors as v,
|
|
187
|
+
} from '@putout/printer';
|
|
188
|
+
|
|
189
|
+
print(ast, {
|
|
190
|
+
visitors: {
|
|
191
|
+
CallExpression(path, printer, semantics) {
|
|
192
|
+
const {print} = printer;
|
|
193
|
+
|
|
194
|
+
if (!path.node.goldstein)
|
|
195
|
+
return v.CallExpression(path, printer, semantics);
|
|
196
|
+
|
|
197
|
+
print('__goldstein');
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### `print`
|
|
204
|
+
|
|
205
|
+
Used in previous example `print` can be used for a couple purposes:
|
|
206
|
+
|
|
207
|
+
- to write `string`;
|
|
208
|
+
- to write `node` when `object` passed;
|
|
209
|
+
- to write `node` when `string` started with `__`;
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
print(ast, {
|
|
213
|
+
visitors: {
|
|
214
|
+
AssignmentPattern(path, {print, maybe}) {
|
|
215
|
+
maybe.write.newline(path.parentPath.isCallExpression());
|
|
216
|
+
print('/* [hello world] */= ');
|
|
217
|
+
print('__right');
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### `maybe`
|
|
224
|
+
|
|
225
|
+
When you need some condition use `maybe`. For example, to add newline only when parent node is `CallExpression` you
|
|
226
|
+
can use `maybe.write.newline(condition)`:
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
print(ast, {
|
|
230
|
+
visitors: {
|
|
231
|
+
AssignmentPattern(path, {write, maybe}) {
|
|
232
|
+
maybe.write.newline(path.parentPath.isCallExpression());
|
|
233
|
+
write(' /* [hello world] */= ');
|
|
234
|
+
write('__right');
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### `write`
|
|
241
|
+
|
|
242
|
+
When you going to output string you can use low-level function `write`:
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
print(ast, {
|
|
246
|
+
visitors: {
|
|
247
|
+
BlockStatement(path, {write}) {
|
|
248
|
+
write('hello');
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### `indent`
|
|
255
|
+
|
|
256
|
+
When you need to add indentation use `indent`, for example when you output body,
|
|
257
|
+
you need to increment indentation, and then decrement it back:
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
print(ast, {
|
|
261
|
+
visitors: {
|
|
262
|
+
BlockStatement(path, {write, indent}) {
|
|
263
|
+
write('{');
|
|
264
|
+
indent.inc();
|
|
265
|
+
indent();
|
|
266
|
+
write('some;');
|
|
267
|
+
indent.dec();
|
|
268
|
+
write('{');
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### `traverse`
|
|
275
|
+
|
|
276
|
+
When you need to traverse node path, you can use `traverse`:
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
print(ast, {
|
|
280
|
+
visitors: {
|
|
281
|
+
AssignmentExpression(path, {traverse}) {
|
|
282
|
+
traverse(path.get('left'));
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
This is the same as `print('__left')` but more low-level, and supports only objects.
|
|
289
|
+
|
|
290
|
+
## Speed Comparison
|
|
291
|
+
|
|
292
|
+
About speed, for file `speed.js`:
|
|
293
|
+
|
|
294
|
+
```js
|
|
295
|
+
const {readFileSync} = require('fs');
|
|
296
|
+
|
|
297
|
+
const putout = require('putout');
|
|
298
|
+
const parser = require('@babel/parser');
|
|
299
|
+
|
|
300
|
+
const code = readFileSync('./lib/tokenize/tokenize.js', 'utf8');
|
|
301
|
+
const ast = parser.parse(code);
|
|
302
|
+
|
|
303
|
+
speed('recast');
|
|
304
|
+
speed('putout');
|
|
305
|
+
|
|
306
|
+
function speed(printer) {
|
|
307
|
+
console.time(printer);
|
|
308
|
+
|
|
309
|
+
for (let i = 0; i < 1000; i++) {
|
|
310
|
+
putout(code, {
|
|
311
|
+
printer,
|
|
312
|
+
plugins: ['remove-unused-variables'],
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
console.timeEnd(printer);
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
With contents of [`tokenize.js`](https://github.com/putoutjs/printer/blob/v1.69.1/lib/tokenize/tokenize.js), we have:
|
|
321
|
+
|
|
322
|
+

|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT
|
package/bin/redlint.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import {writeFile} from 'node:fs/promises';
|
|
3
|
+
import {buildTree} from '../lib/redlint.js';
|
|
4
|
+
|
|
5
|
+
const result = await buildTree(process.cwd());
|
|
6
|
+
const filesystem = JSON.stringify(result, null, 4) + '\n';
|
|
7
|
+
|
|
8
|
+
await writeFile('.filesystem.json', filesystem);
|
package/lib/redlint.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { opendir } from 'node:fs/promises';
|
|
2
|
+
import {relative} from 'node:path';
|
|
3
|
+
import fastGlob from 'fast-glob';
|
|
4
|
+
|
|
5
|
+
const ignore = [
|
|
6
|
+
'node_modules',
|
|
7
|
+
'coverage',
|
|
8
|
+
'.git',
|
|
9
|
+
'.idea',
|
|
10
|
+
'fixture',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export const buildTree = async (filename) => {
|
|
14
|
+
return {
|
|
15
|
+
type: 'directory',
|
|
16
|
+
filename,
|
|
17
|
+
files: await walk(filename)
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function walk(cwd, files = []) {
|
|
22
|
+
const dir = await opendir(cwd);
|
|
23
|
+
|
|
24
|
+
for await (const dirent of dir) {
|
|
25
|
+
const {path, name} = dirent;
|
|
26
|
+
|
|
27
|
+
if (isIgnored(name))
|
|
28
|
+
continue;
|
|
29
|
+
|
|
30
|
+
const filename = `${path}/${name}`;
|
|
31
|
+
|
|
32
|
+
if (dirent.isDirectory()) {
|
|
33
|
+
debugger;
|
|
34
|
+
files.push({
|
|
35
|
+
type: 'directory',
|
|
36
|
+
filename,
|
|
37
|
+
files: await walk(filename),
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
files.push({
|
|
43
|
+
type: 'file',
|
|
44
|
+
filename,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return files;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isIgnored(name) {
|
|
52
|
+
for (const current of ignore) {
|
|
53
|
+
if (RegExp(current).test(name))
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false;
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "redlint",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
|
|
6
|
+
"description": "Lint Filesystem with 🐊Putout",
|
|
7
|
+
"homepage": "https://github.com/putoutjs/redlint#readme",
|
|
8
|
+
"main": "./lib/redlint.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./lib/redlint.js"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git://github.com/putoutjs/redlint.git"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"wisdom": "madrun wisdom",
|
|
18
|
+
"test": "madrun test",
|
|
19
|
+
"test:dts": "madrun test:dts",
|
|
20
|
+
"watch:test": "madrun watch:test",
|
|
21
|
+
"lint": "madrun lint",
|
|
22
|
+
"fresh:lint": "madrun fresh:lint",
|
|
23
|
+
"lint:fresh": "madrun lint:fresh",
|
|
24
|
+
"fix:lint": "madrun fix:lint",
|
|
25
|
+
"coverage": "madrun coverage",
|
|
26
|
+
"coverage:html": "madrun coverage:html",
|
|
27
|
+
"report": "madrun report"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@putout/babel": "^1.1.1",
|
|
31
|
+
"@putout/compare": "^13.0.0",
|
|
32
|
+
"@putout/operate": "^11.0.0",
|
|
33
|
+
"@putout/processor-json": "^7.0.0",
|
|
34
|
+
"fullstore": "^3.0.0",
|
|
35
|
+
"just-snake-case": "^3.2.0",
|
|
36
|
+
"parse-import-specifiers": "^1.0.1",
|
|
37
|
+
"rendy": "^4.0.0"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"putout",
|
|
41
|
+
"redlint",
|
|
42
|
+
"AST",
|
|
43
|
+
"babel",
|
|
44
|
+
"api",
|
|
45
|
+
"traverse",
|
|
46
|
+
"generate"
|
|
47
|
+
],
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@babel/plugin-codemod-object-assign-to-object-spread": "^7.10.4",
|
|
50
|
+
"@putout/plugin-minify": "^5.0.0",
|
|
51
|
+
"@putout/plugin-redlint": "^3.0.0",
|
|
52
|
+
"@putout/plugin-promises": "^13.0.0",
|
|
53
|
+
"@putout/plugin-react-hook-form": "^4.0.0",
|
|
54
|
+
"@putout/plugin-react-hooks": "^5.0.0",
|
|
55
|
+
"acorn": "^8.8.2",
|
|
56
|
+
"c8": "^8.0.0",
|
|
57
|
+
"check-dts": "^0.7.2",
|
|
58
|
+
"escover": "^3.0.0",
|
|
59
|
+
"eslint": "^8.0.1",
|
|
60
|
+
"eslint-plugin-n": "^16.0.0",
|
|
61
|
+
"eslint-plugin-putout": "^21.0.1",
|
|
62
|
+
"estree-to-babel": "^8.0.0",
|
|
63
|
+
"just-kebab-case": "^4.2.0",
|
|
64
|
+
"madrun": "^9.0.0",
|
|
65
|
+
"mock-require": "^3.0.3",
|
|
66
|
+
"montag": "^1.0.0",
|
|
67
|
+
"nodemon": "^3.0.1",
|
|
68
|
+
"putout": "^32.0.4",
|
|
69
|
+
"supertape": "^8.0.0",
|
|
70
|
+
"try-catch": "^3.0.0"
|
|
71
|
+
},
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=16"
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
}
|
|
79
|
+
}
|