@webhandle/backbone-view 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/README.md +167 -0
- package/client-js/event-entry-mapper.js +16 -0
- package/client-js/extract-event-names.js +7 -0
- package/client-js/generate-id.js +12 -0
- package/client-js/index.js +5 -0
- package/client-js/pick.js +14 -0
- package/client-js/view.js +199 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Webhandle Backbone View
|
|
2
|
+
|
|
3
|
+
A replacement for Backbone's View.
|
|
4
|
+
|
|
5
|
+
I really like Backbone's View. It's a perfect level of abstraction and container for when a framework
|
|
6
|
+
is too big and managing a few different object types becomes messy.
|
|
7
|
+
|
|
8
|
+
However, it requires jQuery and Underscore, which add a staggering 600k to the bundle making it unsuitable for the
|
|
9
|
+
creation of little modules. Additionally, its code was written to run on older IE browsers and still
|
|
10
|
+
uses that packaging and those techniques.
|
|
11
|
+
|
|
12
|
+
While that level of backward compatibility can be useful, I want to write code that uses classes,
|
|
13
|
+
modules, etc, and want my tools to support that.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @webhandle/backbone-view
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It's about 1027 bytes zipped as part of a webpack bundle, so about 1% of the size of Backbone and its
|
|
24
|
+
dependencies.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Import like
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const View = require('@webhandle/backbone-view').View
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
or
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import { View } from '@webhandle/backbone-view'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
[Backbone's actual documentation](https://backbonejs.org/#View) is still pretty descriptive of what this does.
|
|
43
|
+
But in brief, the basic process is to extend View and define how the data will be rendered and how events will be handled.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Setting callbacks to handle events is done by creating an events object, where `this.events` is a hash like:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{"event selector": "callback"}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Events are things like `click` and `mousedown`. Selectors are standard css selectors like `button`, `footer .ok`, or `.ok`. Each
|
|
53
|
+
selector is assumed to target only elements within the view. A special selector value, `.`, is used to indicate the
|
|
54
|
+
root element of the view.
|
|
55
|
+
|
|
56
|
+
Events are handled by event listeners on the view's root element. That means that while you have to have the
|
|
57
|
+
events defined when the view is created, you can change the content of the view's dom section however you want and
|
|
58
|
+
whenever you want. Events on those new elements will still get handled.
|
|
59
|
+
|
|
60
|
+
Callbacks are either a string, which refers to a method of the view, or functions. In either case the method/function
|
|
61
|
+
will be called with the view as the `this` object. These functions will receive the dom event and the element matching
|
|
62
|
+
the selector which the event bubbled through. This may not be the same as the `evt.target` since a child element of the matching
|
|
63
|
+
element may have been the actual thing clicked (or whatever). Essentially, the second argument is what the `evt.target` would
|
|
64
|
+
have been if the event listener had been added directly to that element instead of the view root.
|
|
65
|
+
|
|
66
|
+
Rendering (via the `render` method) is the most free from. It requires that the contents of `this.el` (the view's root element)
|
|
67
|
+
get modified to show content appropriate to `this.model`. You can use any templating library or no templating library at all.
|
|
68
|
+
|
|
69
|
+
Include the view into the page by adding the view object's `el` (the root element of the view) to any place in the dom. There
|
|
70
|
+
are two convience functions `appendTo` and `replaceContentsOf`.
|
|
71
|
+
|
|
72
|
+
And don't forget to call `render`, either before or after the element is added to the dom, or you won't see any content.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { View } from '@webhandle/backbone-view'
|
|
77
|
+
|
|
78
|
+
class DollarView extends View {
|
|
79
|
+
preinitialize() {
|
|
80
|
+
this.events = {
|
|
81
|
+
'click .one': 'oneClicked',
|
|
82
|
+
'click .': function (evt) {
|
|
83
|
+
console.log(`${this.model} it got clicked`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
render() {
|
|
89
|
+
this.el.innerHTML = "$" + `<span id="${this.id}one" class="one"><span id="${this.id}two" class="two">${parseFloat(this.model)}</span></span>`
|
|
90
|
+
return this
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
oneClicked(evt, selected) {
|
|
94
|
+
console.log(`one clicked ${selected.id}`)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let one = new DollarView({
|
|
99
|
+
model: 123.45
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
one.render()
|
|
103
|
+
one.appendTo(document.body)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
A view will accept the options 'model', 'el', 'id', 'attributes', 'className', 'tagName', and 'events'.
|
|
107
|
+
It's possible to pass these in as options to an instance or as options via the subclass's constructor.
|
|
108
|
+
However, I think it looks cleaner to implement the `preinitialize` function, which is called before
|
|
109
|
+
the view creates any elements.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## Differences from Backbone
|
|
113
|
+
|
|
114
|
+
As mentioned above, there are no dependencies. The View also lacks some of the functions used by Backbone
|
|
115
|
+
for interaction with its Model and Collections components. Since there is no jQuery, some of the members
|
|
116
|
+
like `this.$el` don't exist and events will be plain HTML dom events.
|
|
117
|
+
|
|
118
|
+
Backbone also limits which members of the options will be added to the View instance object to those listed
|
|
119
|
+
above. That seems unnecessarily limiting so this View adds all members.
|
|
120
|
+
|
|
121
|
+
While broadly compatible, I wouldn't expect the behaviors to be identical.
|
|
122
|
+
|
|
123
|
+
## An Only Slightly More Complicated Example
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
import { View } from '@webhandle/backbone-view'
|
|
127
|
+
|
|
128
|
+
class DollarView extends View {
|
|
129
|
+
preinitialize() {
|
|
130
|
+
this.events = {
|
|
131
|
+
'click .one': 'oneClicked'
|
|
132
|
+
, 'click .math-button': 'doMath'
|
|
133
|
+
, 'click .': function (evt) {
|
|
134
|
+
console.log(`${this.model} it got clicked`)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
render() {
|
|
140
|
+
this.el.innerHTML = "$" + `<span id="${this.id}one" class="one"><span id="${this.id}two" class="two">${parseFloat(this.model)}</span></span><button class="add-one math-button">Add One</button><button class="substract-one math-button">Subtract One</button>`
|
|
141
|
+
return this
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
oneClicked(evt, selected) {
|
|
145
|
+
console.log(`one clicked ${selected.id}`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
doMath(evt, selected) {
|
|
149
|
+
if(selected.classList.contains('add-one')) {
|
|
150
|
+
this.model = this.model + 1
|
|
151
|
+
}
|
|
152
|
+
else if(selected.classList.contains('subtract-one')) {
|
|
153
|
+
this.model = this.model - 1
|
|
154
|
+
}
|
|
155
|
+
this.el.querySelector('.two').innerHTML = parseFloat(this.model)
|
|
156
|
+
console.log(this.randomInfo)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let one = new DollarView({
|
|
161
|
+
model: 1.45
|
|
162
|
+
, randomInfo: 20
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
one.render()
|
|
166
|
+
one.appendTo(document.body)
|
|
167
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default function eventEntryMapper([key, value]) {
|
|
2
|
+
key = key.trim()
|
|
3
|
+
let parts = key.split(' ')
|
|
4
|
+
let event = parts.shift().trim()
|
|
5
|
+
let selector = parts.join(' ').trim()
|
|
6
|
+
|
|
7
|
+
if(typeof value === 'string') {
|
|
8
|
+
value = value.trim()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
event: event,
|
|
13
|
+
selector: selector,
|
|
14
|
+
handler: value
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a random string id in the browser. Will probably not work
|
|
3
|
+
* on the server.
|
|
4
|
+
* @returns A base64 web url safe string
|
|
5
|
+
*/
|
|
6
|
+
export default function generateId() {
|
|
7
|
+
let array = new Uint8Array(32)
|
|
8
|
+
window.crypto.getRandomValues(array)
|
|
9
|
+
let value = btoa(array)
|
|
10
|
+
value = value.replace(/\//g, "_").replace(/\+/g, "-").replace(/=+$/, "")
|
|
11
|
+
return value
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicates and returns an object with only the speicified keys.
|
|
3
|
+
* @param {object} foucs
|
|
4
|
+
* @param {string[]} keys
|
|
5
|
+
*/
|
|
6
|
+
export default function pick(focus = {}, keys = []) {
|
|
7
|
+
let clone = {}
|
|
8
|
+
for(let key of keys) {
|
|
9
|
+
if(typeof focus[key] !== 'undefined'){
|
|
10
|
+
clone[key] = focus[key]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return clone
|
|
14
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import generateId from "./generate-id.js"
|
|
2
|
+
// import pick from "./pick.js"
|
|
3
|
+
import eventEntryMapper from "./event-entry-mapper.js"
|
|
4
|
+
import extractEventNames from "./extract-event-names.js"
|
|
5
|
+
|
|
6
|
+
let defaultOptions = {
|
|
7
|
+
// The default `tagName` of a View's element is `"div"`.
|
|
8
|
+
tagName: 'div'
|
|
9
|
+
|
|
10
|
+
, events: {}
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
let viewOptions = ['model', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A way to connect data to be displayed, a way to display it, and an organization
|
|
17
|
+
* of functions to handle events.
|
|
18
|
+
*/
|
|
19
|
+
export class View {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.id = generateId()
|
|
22
|
+
Object.assign(this, defaultOptions)
|
|
23
|
+
this.preinitialize.apply(this, arguments);
|
|
24
|
+
Object.assign(this, options)
|
|
25
|
+
this._ensureElement()
|
|
26
|
+
this.initialize.apply(this, arguments);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* preinitialize is an empty function by default. You can override it with a function
|
|
32
|
+
* or object. preinitialize will run before any instantiation logic is run in the View
|
|
33
|
+
*/
|
|
34
|
+
preinitialize() { }
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Initialize is an empty function by default. Override it with your own
|
|
38
|
+
* initialization logic.
|
|
39
|
+
*/
|
|
40
|
+
initialize() { }
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* **render** is the core function that your view should override, in order
|
|
44
|
+
* to populate its element (`this.el`), with the appropriate HTML. The
|
|
45
|
+
* convention is for **render** to always return `this`.
|
|
46
|
+
* @returns this
|
|
47
|
+
*/
|
|
48
|
+
render() {
|
|
49
|
+
return this
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Removes the element from the dom. Does not disable event listeners
|
|
54
|
+
*/
|
|
55
|
+
remove() {
|
|
56
|
+
this.el.parentElement.removeChild(this.el)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Adds this view as a child to a containing element. Nothing special is going on here.
|
|
61
|
+
* This is just a shortcut for container.appendChild
|
|
62
|
+
* @param {Element} container
|
|
63
|
+
*/
|
|
64
|
+
appendTo(container) {
|
|
65
|
+
container.appendChild(this.el)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Clears the contents of the container and adds this view.
|
|
70
|
+
* @param {Element} container
|
|
71
|
+
*/
|
|
72
|
+
replaceContentsOf(container) {
|
|
73
|
+
container.innerHTML = ''
|
|
74
|
+
this.appendTo(container)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set the element for this view, and if new, adds listeners to it in accordance
|
|
79
|
+
* with the "events" member.
|
|
80
|
+
* @param {Element} el The dom element which will be the root of this view
|
|
81
|
+
* @returns this
|
|
82
|
+
*/
|
|
83
|
+
setElement(el) {
|
|
84
|
+
if (this.el !== el) {
|
|
85
|
+
this.el = el
|
|
86
|
+
this._addListeners()
|
|
87
|
+
}
|
|
88
|
+
return this
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Produces a DOM element to be assigned to your view. Exposed for
|
|
93
|
+
* subclasses using an alternative DOM manipulation API.
|
|
94
|
+
* @param {string} name The element tag name
|
|
95
|
+
* @returns The dom element
|
|
96
|
+
*/
|
|
97
|
+
_createElement(name) {
|
|
98
|
+
let el = document.createElement(name)
|
|
99
|
+
el.view = this
|
|
100
|
+
return el
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Ensures that the element exists. Applies attributes and className
|
|
105
|
+
* to it regardless
|
|
106
|
+
*/
|
|
107
|
+
_ensureElement() {
|
|
108
|
+
if (!this.el) {
|
|
109
|
+
this.setElement(this._createElement(this.tagName))
|
|
110
|
+
}
|
|
111
|
+
this._setAttributes()
|
|
112
|
+
if (this.className) {
|
|
113
|
+
this.el.classList.add(this.className)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Set attributes from a hash on this view's element. Exposed for
|
|
119
|
+
* subclasses using an alternative DOM manipulation API.
|
|
120
|
+
* @param {object} attributes
|
|
121
|
+
*/
|
|
122
|
+
_setAttributes(attributes) {
|
|
123
|
+
if (this.attributes) {
|
|
124
|
+
for (let [key, value] of Object.entries(this.attributes)) {
|
|
125
|
+
this.el.setAttribute(key, value)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
*
|
|
132
|
+
* Set callbacks, where `this.events` is a hash of
|
|
133
|
+
* *{"event selector": "callback"}*
|
|
134
|
+
*
|
|
135
|
+
* {
|
|
136
|
+
* 'mousedown .title': 'edit',
|
|
137
|
+
* 'click .button': 'save',
|
|
138
|
+
* 'click .open': function(e) { ... },
|
|
139
|
+
* 'keydown .': 'handleKey'
|
|
140
|
+
* }
|
|
141
|
+
* pairs. Callbacks will be bound to the view, with `this` set properly.
|
|
142
|
+
*
|
|
143
|
+
*
|
|
144
|
+
* Note that the selector `.` will match the root element and can be used
|
|
145
|
+
* as a final chance to handle events or for events like an escape key
|
|
146
|
+
* which are essentially global to the widget.
|
|
147
|
+
*
|
|
148
|
+
*/
|
|
149
|
+
_addListeners() {
|
|
150
|
+
this.eventTriggers = Object.entries(this.events).map(eventEntryMapper)
|
|
151
|
+
let eventNames = extractEventNames(this.eventTriggers)
|
|
152
|
+
|
|
153
|
+
for(let eventName of eventNames) {
|
|
154
|
+
this.el.addEventListener(eventName, this._eventHandler.bind(this))
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the elements from the view which match the selector
|
|
160
|
+
* @param {string} selector A css selector. `.` will select the root element
|
|
161
|
+
* @returns An array of elements
|
|
162
|
+
*/
|
|
163
|
+
_getCandidates(selector) {
|
|
164
|
+
if(selector === '.') {
|
|
165
|
+
return [this.el]
|
|
166
|
+
}
|
|
167
|
+
return Array.from(this.el.querySelectorAll(selector))
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Handles all events for all elements within the view. It attempts to find a
|
|
172
|
+
* trigger matching the event and then process it. It will match and invoke
|
|
173
|
+
* only one trigger.
|
|
174
|
+
* @param {Event} evt
|
|
175
|
+
*/
|
|
176
|
+
_eventHandler(evt) {
|
|
177
|
+
for(let trigger of this.eventTriggers) {
|
|
178
|
+
if(evt.type == trigger.event) {
|
|
179
|
+
let candidates = this._getCandidates(trigger.selector)
|
|
180
|
+
let found = null
|
|
181
|
+
for(let candidate of candidates) {
|
|
182
|
+
if(candidate === evt.target || candidate.contains(evt.target)) {
|
|
183
|
+
found = candidate
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if(found) {
|
|
188
|
+
if(typeof trigger.handler === 'string') {
|
|
189
|
+
this[trigger.handler].call(this, evt, found)
|
|
190
|
+
}
|
|
191
|
+
else if(typeof trigger.handler === 'function') {
|
|
192
|
+
trigger.handler.call(this, evt, found)
|
|
193
|
+
}
|
|
194
|
+
break
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webhandle/backbone-view",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "client-js/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node_modules/mocha/bin/mocha",
|
|
8
|
+
"less-build": "npx lessc --source-map --source-map-include-source less/pages.less public/css/pages.css",
|
|
9
|
+
"less-compress": "npx uglifycss public/css/pages.css > public/css/pages.min.css",
|
|
10
|
+
"client-js-build": "npx webpack --config pages.webpack.cjs",
|
|
11
|
+
"client-js-compress": "npx uglifyjs public/js/pages.cjs -o public/js/pages.min.cjs -c --source-map url=public/js/pages.min.cjs.map",
|
|
12
|
+
"client-js-browserify-build": "npm run client-js-pages-browserify-build",
|
|
13
|
+
"client-js-pages-browserify build": "npx browserify client-js/pages.cjs --debug | npx exorcist public/js/pages.cjs.map > public/js/pages.cjs",
|
|
14
|
+
"dev-less-watch": "onchange 'less/**/*.less' -- npm run less-build",
|
|
15
|
+
"dev-client-js-watch": "onchange 'client-js/**/*js' 'client-js-test/**/*js' -- npm run client-js-build",
|
|
16
|
+
"dev-server-js-watch": "onchange 'server-js/**/*js' -- pm2 restart $npm_package_name-web",
|
|
17
|
+
"start": "node ./web-server.js",
|
|
18
|
+
"testDebug": "node --inspect-brk node_modules/mocha/bin/mocha",
|
|
19
|
+
"bg": "parallelshell 'npm run dev-less-watch' 'npm run dev-client-js-watch'",
|
|
20
|
+
"pm2-bg": "parallelshell 'npm run dev-less-watch' 'npm run dev-client-js-watch' 'npm run dev-server-js-watch'",
|
|
21
|
+
"dev": "parallelshell 'npm run start' 'npm run dev-less-watch' 'npm run dev-client-js-watch'"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/EmergentIdeas/webhandle-backbone-view.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/EmergentIdeas/webhandle-backbone-view/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/EmergentIdeas/webhandle-backbone-view#readme",
|
|
31
|
+
"keywords": [
|
|
32
|
+
"Backbone",
|
|
33
|
+
"View"
|
|
34
|
+
],
|
|
35
|
+
"author": "Dan Kolz",
|
|
36
|
+
"license": "ISC",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"browserify": "^14.4.0",
|
|
39
|
+
"chai": "^4.3.4",
|
|
40
|
+
"exorcist": "^2.0.0",
|
|
41
|
+
"express": "^4.17.1",
|
|
42
|
+
"file-sink": "^1.0.4",
|
|
43
|
+
"filter-log": "0.0.5",
|
|
44
|
+
"input-value-injector": "^1.0.8",
|
|
45
|
+
"less": "^3.10.3",
|
|
46
|
+
"mocha": "^9.1.3",
|
|
47
|
+
"node-polyfill-webpack-plugin": "^2.0.1",
|
|
48
|
+
"onchange": "^3.2.1",
|
|
49
|
+
"parallelshell": "3.0.1",
|
|
50
|
+
"tripartite": "^1.1.1",
|
|
51
|
+
"uglify-js": "^3.17.4",
|
|
52
|
+
"webhandle": "^1.0.32",
|
|
53
|
+
"webhandle-js-widget-setup": "^1.0.5",
|
|
54
|
+
"webpack-cli": "^5.1.4"
|
|
55
|
+
},
|
|
56
|
+
"browserify": {
|
|
57
|
+
"transform": [
|
|
58
|
+
"tripartite/browserify-transform"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"files": [
|
|
62
|
+
"/client-js",
|
|
63
|
+
"README.md"
|
|
64
|
+
],
|
|
65
|
+
"type": "module"
|
|
66
|
+
}
|