leanweb 1.0.4

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.
@@ -0,0 +1,237 @@
1
+ const { execSync } = require('child_process');
2
+ const sass = require('sass');
3
+ const path = require('path');
4
+ const net = require('net');
5
+
6
+ const dirs = {
7
+ src: 'src',
8
+ build: 'build',
9
+ serve: 'serve',
10
+ dist: 'dist',
11
+ electron: 'electron',
12
+ };
13
+
14
+ module.exports.exec = command => execSync(command, { encoding: 'utf8', stdio: 'inherit' });
15
+
16
+ module.exports.buildCSS = (scssString, currentPaths) => {
17
+ if (scssString.trim()) {
18
+ const includePaths = [currentPaths, path.resolve(process.cwd(), dirs.build), path.resolve(process.cwd(), 'node_modules')];
19
+ const cssResult = sass.renderSync({ data: scssString, includePaths });
20
+ return cssResult.css.toString().trim();
21
+ }
22
+ return '';
23
+ };
24
+
25
+ module.exports.getComponentName = cmp => {
26
+ const indexOfLastSlash = cmp.lastIndexOf('/');
27
+ if (indexOfLastSlash > -1) {
28
+ return cmp.substring(indexOfLastSlash + 1);
29
+ }
30
+ return cmp;
31
+ };
32
+
33
+ module.exports.getPathLevels = filePath => {
34
+ filePath = path.normalize(filePath);
35
+ const numSlashes = filePath.replace(/[^\/]/g, '').length;
36
+ let ret = '';
37
+ for (let i = 0; i < numSlashes; ++i) {
38
+ ret += '../';
39
+ }
40
+ return ret;
41
+ };
42
+
43
+ module.exports.throttle = (callback, limit = 100) => {
44
+ let wait = false;
45
+ return function () {
46
+ if (!wait) {
47
+ wait = true;
48
+ setTimeout(() => {
49
+ wait = false;
50
+ callback.apply(null, arguments);
51
+ }, limit);
52
+ }
53
+ };
54
+ };
55
+
56
+ module.exports.getWebPackConfig = (outputDir, project) => {
57
+ return {
58
+ entry: process.cwd() + `/${dirs.build}/${project.name}.js`,
59
+ output: {
60
+ path: process.cwd() + `/${outputDir}/`,
61
+ filename: `${project.name}.js`,
62
+ },
63
+ module: {
64
+ rules: [{
65
+ test: path.resolve(process.cwd()),
66
+ exclude: /node_modules/,
67
+ loader: require.resolve('babel-loader'),
68
+ options: {
69
+ presets: [require.resolve('@babel/preset-env'), {
70
+ plugins: [
71
+ '@babel/plugin-transform-runtime'
72
+ ].map(require.resolve)
73
+ }]
74
+ },
75
+ }, {
76
+ test: /\.(scss|sass)$/i,
77
+ use: [
78
+ {
79
+ loader: require.resolve('css-loader'),
80
+ },
81
+ {
82
+ loader: require.resolve('sass-loader'),
83
+ options: {
84
+ sassOptions: {
85
+ includePaths: [path.resolve(process.cwd(), 'node_modules')],
86
+ }
87
+ }
88
+ },
89
+ ],
90
+ }, {
91
+ test: /\.json$/i,
92
+ loader: require.resolve('json5-loader'),
93
+ options: {
94
+ esModule: true,
95
+ },
96
+ type: 'javascript/auto',
97
+ }, {
98
+ loader: require.resolve('raw-loader'),
99
+ exclude: [
100
+ /\.(js|mjs|jsx|ts|tsx)$/i,
101
+ /\.(json|json5)$/i,
102
+ /\.(css|scss|sass)$/i
103
+ ],
104
+ }]
105
+ },
106
+ }
107
+ };
108
+
109
+ module.exports.portInUse = (port, address = '127.0.0.1') => {
110
+ return new Promise((resolve, reject) => {
111
+ const server = net.createServer(socket => socket.pipe(socket));
112
+
113
+ server.listen(port, address);
114
+ server.on('error', e => {
115
+ resolve(true);
116
+ });
117
+ server.on('listening', e => {
118
+ server.close();
119
+ resolve(false);
120
+ });
121
+ });
122
+ };
123
+
124
+ module.exports.dirs = dirs;
125
+
126
+ const initNote = `Usage: leanweb init or leanweb init project-name
127
+ leanweb init will initialize a leanweb project with the name of the current
128
+ working directory, otherwise, if a project-name is provided, the provided
129
+ project-name will be used as the leanweb project name.
130
+
131
+ leanweb init command will create src/leanweb.json file, which looks like:
132
+ {
133
+ "name": "demo",
134
+ "version": "0.4.5",
135
+ "components": [
136
+ "demo-root",
137
+ ],
138
+ "resources": [
139
+ "resources/"
140
+ ]
141
+ }
142
+ where demo is the project name.
143
+
144
+ A src/ directory will be created, and the top level web component demo-root
145
+ will be created. demo-root web component contains 3 files:
146
+
147
+ root.html
148
+ root.js
149
+ root.scss
150
+
151
+ Under src/ directory, global-styles.scss is created for global styling.
152
+ `;
153
+
154
+ const generateNote = `Usage: leanweb generate component-name
155
+ For example leanweb g login will create demo-login web component in
156
+ src/components directory. The leanweb.json will be updated to look like:
157
+
158
+ {
159
+ "name": "demo",
160
+ "version": "0.4.5",
161
+ "components": [
162
+ "root",
163
+ "login"
164
+ ],
165
+ "resources": [
166
+ "resources/"
167
+ ]
168
+ }
169
+
170
+ demo-login web component will contain 3 files:
171
+
172
+ login.html
173
+ login.js
174
+ login.scss
175
+
176
+ Now, the demo-login component can be added in root.html as follows:
177
+ <demo-login></demo-login>
178
+ `;
179
+
180
+ const serveNote = `Usage: leabweb [env] serve or lw s [env]
181
+ Running this command will start the dev server and open the app in a new
182
+ browser window. Any chances to the source code will cause the dev server to
183
+ reload the app and the browser will be automatically refreshed.
184
+ `;
185
+
186
+ const buildNote = `Usage: leanweb build [env]
187
+ This will build the app and the output files will be stored in the build/
188
+ directory. In this phase, the build doesn't transpile the app code. So likely
189
+ the build file will only work with latest Chrome. However, the dist will
190
+ produce output for most desktop and mobile browsers.
191
+ `;
192
+
193
+ const distNote = `Usage: leanweb dist [env]
194
+ This will transpile the source code and produce output compatible with most
195
+ desktop and mobile browsers. The output will be stored in dist/ directory.
196
+ `;
197
+
198
+ const cleanNote = `Usage: leanweb clean
199
+ This will remove the build and dist directory.
200
+ `;
201
+
202
+ const upgradeNote = `Usage: leanweb upgrade
203
+ This will upgrade leanweb runtime in the src/lib directory.
204
+ `;
205
+
206
+ const electronNote = `Usage: leanweb electron [env]
207
+ This will run the app as native desktop app using Electron.
208
+ `;
209
+
210
+ const destroyNote = `Usage leanweb destroy project-name
211
+ This will remove the src/, build/ and dist/ directory. Please
212
+ note the src directory will be deleted by this command.
213
+ `
214
+
215
+ const helpNote = `Usage: leanweb help target-name
216
+ This will show help information of each target. All target names could be
217
+ abbreviated as long as there is no ambiguity. For example:
218
+ leanweb help build is the same as leanweb h b
219
+ leanweb generate login is the same as leanweb g login
220
+ `;
221
+
222
+ const versionNote = `Usage: leanweb version
223
+ Print version information for leanweb.`;
224
+
225
+ module.exports.targets = {
226
+ 'init': { file: 'init.js', note: initNote },
227
+ 'generate': { file: 'generate.js', note: generateNote },
228
+ 'serve': { file: 'serve.js', note: serveNote },
229
+ 'build': { file: 'build.js', note: buildNote },
230
+ 'dist': { file: 'dist.js', note: distNote },
231
+ 'upgrade': { file: 'upgrade.js', note: upgradeNote },
232
+ 'clean': { file: 'clean.js', note: cleanNote },
233
+ 'electron': { file: 'electron.js', note: electronNote },
234
+ 'destroy': { file: 'destroy.js', note: destroyNote },
235
+ 'help': { file: 'help.js', note: helpNote },
236
+ 'version': { file: 'version.js', note: versionNote },
237
+ };
@@ -0,0 +1,2 @@
1
+ const packageJSON = require('../package.json');
2
+ console.log(packageJSON.name, packageJSON.version);
package/leanweb.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ (async () => {
4
+
5
+ const fs = require('fs');
6
+ const semver = require('semver');
7
+ const utils = require('./commands/utils.js');
8
+
9
+ const args = process.argv;
10
+
11
+ if (args.length < 3) {
12
+ utils.exec('npx lw help');
13
+ return;
14
+ }
15
+
16
+ let target = args[2];
17
+
18
+ const targetCandidates = Object.keys(utils.targets).filter(t => t.startsWith(target));
19
+ if (targetCandidates.length === 0) {
20
+ console.error(`Error: target ${target} not found.`);
21
+ return;
22
+ } else if (targetCandidates.length > 1) {
23
+ targetCandidates.forEach(t => {
24
+ console.log(t);
25
+ });
26
+ return;
27
+ }
28
+
29
+ target = targetCandidates[0];
30
+
31
+ const leanwebJSONExisted = fs.existsSync(`${process.cwd()}/${utils.dirs.src}/leanweb.json`);
32
+
33
+ if (!leanwebJSONExisted && target !== 'init' && target !== 'help' && target !== 'version') {
34
+ console.error('Error: leanweb.json not found.');
35
+ return;
36
+ }
37
+
38
+ if (leanwebJSONExisted && target === 'version' || target === 'serve' || target === 'dist' || target === 'electron') {
39
+ const leanwebPackageJSON = require(`${__dirname}/package.json`);
40
+ const projectLeanwebJSON = require(`${process.cwd()}/${utils.dirs.src}/leanweb.json`);
41
+ const upgradeAvailable = semver.gt(leanwebPackageJSON.version, projectLeanwebJSON.version);
42
+ if (upgradeAvailable) {
43
+ console.log(`New version of leanweb lib (${projectLeanwebJSON.version} => ${leanwebPackageJSON.version}) is available. Please consider
44
+ running 'lw upgrade' to upgrade your project leanweb lib.`);
45
+ }
46
+
47
+ const projectTooNew = semver.gt(projectLeanwebJSON.version, leanwebPackageJSON.version);
48
+ if (projectTooNew) {
49
+ console.log(`Poject version of leanweb (${projectLeanwebJSON.version} > ${leanwebPackageJSON.version}) is newer than local leanweb tools version.
50
+ Please consider running 'npm i leanweb -g' to upgrade your local leanweb tools.`);
51
+ }
52
+ }
53
+ const targetData = utils.targets[target];
54
+ const command = `node --trace-deprecation ${__dirname}/commands/${targetData.file} ${args.slice(3).join(' ')}`;
55
+ utils.exec(command);
56
+ })();
@@ -0,0 +1,113 @@
1
+ const parse5 = require('parse5');
2
+ const parser = require('@babel/parser');
3
+
4
+ let astKey = 0;
5
+
6
+ const removeASTLocation = ast => {
7
+ if (Array.isArray(ast)) {
8
+ ast.forEach(a => removeASTLocation(a));
9
+ } else if (typeof ast === 'object') {
10
+ delete ast['loc'];
11
+ delete ast['start'];
12
+ delete ast['end'];
13
+ const values = Object.values(ast).filter(v => Array.isArray(v) || typeof v === 'object');
14
+ removeASTLocation(values);
15
+ }
16
+ };
17
+
18
+ const getAST = expr => {
19
+ const parsedProgram = parser.parse(expr).program;
20
+ if (parsedProgram.directives.length > 0 && parsedProgram.body.length === 0) {
21
+ return parsedProgram.directives;
22
+ }
23
+ return parsedProgram.body;
24
+ };
25
+
26
+ const walkNode = (node, interpolation) => {
27
+ node.attrs && node.attrs.forEach(attr => {
28
+ const { startLine, endLine } = node.sourceCodeLocation;
29
+ const loc = { startLine, endLine };
30
+ const key = `${++astKey}`;
31
+ if (attr.name === 'lw-false' || attr.name === 'lw-context' || attr.name === 'lw-for-parent') {
32
+ // this should never happen
33
+ console.assert(false, attr.name);
34
+ // no op
35
+ } else if (attr.name === 'lw') {
36
+ node.attrs.push({ name: 'lw-elem', value: '' });
37
+ let expr = '';
38
+ if (node.childNodes) {
39
+ const exprNode = node.childNodes.find(childNode => childNode.nodeName === '#text');
40
+ expr = exprNode ? exprNode.value : '';
41
+ }
42
+ const ast = getAST(expr);
43
+ removeASTLocation(ast);
44
+ interpolation[key] = { ast, loc };
45
+ node.childNodes.length = 0;
46
+ attr.value = key;
47
+ } else if (attr.name === ('lw-for')) {
48
+ node.attrs.push({ name: 'lw-elem', value: '' });
49
+ const matched = attr.value.match(/(.+)\s+in\s+(.+)/);
50
+ const itemIndex = matched[1].split(',');
51
+ const itemExpr = itemIndex[0].trim();
52
+ let indexExpr;
53
+ if (itemIndex.length > 1) {
54
+ indexExpr = itemIndex[1].trim();
55
+ }
56
+ const itemsExpr = matched[2];
57
+ const astItems = getAST(itemsExpr);
58
+ removeASTLocation(astItems);
59
+ interpolation[key] = { astItems, loc, itemExpr, indexExpr, itemsExpr };
60
+ attr.value = key;
61
+ } else if (attr.name === ('lw-model')) {
62
+ node.attrs.push({ name: 'lw-elem-bind', value: '' });
63
+ node.attrs.push({ name: 'lw-elem', value: '' });
64
+ const ast = getAST(attr.value);
65
+ removeASTLocation(ast);
66
+ interpolation[key] = { ast, loc };
67
+ attr.value = key;
68
+ } else if (attr.name.startsWith('lw-on:') || attr.name.startsWith('lw-class:') || attr.name.startsWith('lw-bind:') || attr.name.startsWith('lw-input:')) {
69
+ if (attr.name.startsWith('lw-on:') || attr.name.startsWith('lw-input:')) {
70
+ node.attrs.push({ name: 'lw-elem-bind', value: '' });
71
+ }
72
+
73
+ node.attrs.push({ name: 'lw-elem', value: '' });
74
+ const lw = attr.name.split(':');
75
+ const lwType = lw[0];
76
+ const lwValue = lw[1];
77
+
78
+ const ast = getAST(attr.value);
79
+ removeASTLocation(ast);
80
+ interpolation[key] = { ast, loc, lwType, lwValue };
81
+
82
+ attr.value = key;
83
+ } else if (attr.name.startsWith('lw-')) {
84
+ node.attrs.push({ name: 'lw-elem', value: '' });
85
+ const ast = getAST(attr.value);
86
+ removeASTLocation(ast);
87
+ interpolation[key] = { ast, loc };
88
+ attr.value = key;
89
+ }
90
+ });
91
+ node.childNodes && node.childNodes.forEach(childNode => walkNode(childNode, interpolation));
92
+ };
93
+
94
+ const parse = html => {
95
+ const ast = {};
96
+ const doc = parse5.parseFragment(html, { sourceCodeLocationInfo: true });
97
+ walkNode(doc, ast);
98
+ html = parse5.serialize(doc);
99
+ ast.html = html;
100
+ return ast;
101
+ };
102
+
103
+ module.exports = { parse };
104
+
105
+
106
+ // const html = `<div>
107
+ // <span class="x" lw>/a/;(1+(2+3))</span>
108
+ // <span lw-on:click="a">dddd</span>
109
+ // <span lw></span>
110
+ // </div>`;
111
+
112
+ // const result = parse(html);
113
+ // console.log(JSON.stringify(result, null, 2));
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "leanweb",
3
+ "version": "1.0.4",
4
+ "description": "Builds framework agnostic web components.",
5
+ "bin": {
6
+ "leanweb": "leanweb.js",
7
+ "lw": "leanweb.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/elgs/leanweb.git"
12
+ },
13
+ "homepage": "https://leanweb.app",
14
+ "keywords": [
15
+ "leanweb",
16
+ "lw",
17
+ "web components"
18
+ ],
19
+ "author": "Qian Chen",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "@babel/core": "^7.15.5",
23
+ "@babel/parser": "^7.15.6",
24
+ "@babel/plugin-transform-runtime": "^7.15.0",
25
+ "@babel/preset-env": "^7.15.6",
26
+ "babel-loader": "^8.2.2",
27
+ "clean-css": "^5.1.5",
28
+ "css-loader": "^6.2.0",
29
+ "fs-extra": "^10.0.0",
30
+ "globby": "^11.0.4",
31
+ "html-minifier": "^4.0.0",
32
+ "isomorphic-git": "^1.10.0",
33
+ "json5-loader": "^4.0.1",
34
+ "node-watch": "^0.7.1",
35
+ "parse5": "^6.0.1",
36
+ "raw-loader": "^4.0.2",
37
+ "sass": "^1.41.1",
38
+ "sass-loader": "^12.1.0",
39
+ "semver": "^7.3.5",
40
+ "webpack": "^5.53.0",
41
+ "webpack-dev-server": "^4.2.1"
42
+ }
43
+ }
@@ -0,0 +1,48 @@
1
+ import LWElement from './../../${pathLevels}lib/lw-element.js';
2
+ import ast from './ast.js';
3
+
4
+ customElements.define('${projectName}-${component}',
5
+ class extends LWElement { // LWElement extends HTMLElement
6
+ constructor() {
7
+ super(ast);
8
+ }
9
+
10
+ // derived from LWElement
11
+ // domReady() {
12
+ // console.log('Dom is ready');
13
+ // }
14
+
15
+ // inputReady() {
16
+ // console.log('input is ready');
17
+ // }
18
+
19
+ // Called when the urlHash changes. This could be useful to update the
20
+ // DOM on component routing.
21
+ // urlHashChanged() {
22
+ // // update component DOM
23
+ // this.update();
24
+ // }
25
+
26
+ // derived from HTMLElement
27
+ // connectedCallback() {
28
+ // console.log(this.isConnected);
29
+ // console.log('Element added to page.');
30
+ // }
31
+
32
+ // disconnectedCallback() {
33
+ // console.log('Element removed from page.');
34
+ // }
35
+
36
+ // adoptedCallback() {
37
+ // console.log('Element moved to new page.');
38
+ // }
39
+
40
+ // static get observedAttributes() {
41
+ // return [];
42
+ // }
43
+
44
+ // attributeChangedCallback(name, oldValue, newValue) {
45
+ // console.log(name, oldValue, newValue);
46
+ // }
47
+ }
48
+ );
@@ -0,0 +1,43 @@
1
+ const { app, BrowserWindow } = require('electron');
2
+
3
+ const createWindow = () => {
4
+ // Create the browser window.
5
+ const win = new BrowserWindow({
6
+ width: 640,
7
+ height: 480,
8
+ webPreferences: {
9
+ nodeIntegration: true
10
+ }
11
+ });
12
+
13
+ // and load the index.html of the app.
14
+ win.loadFile('index.html');
15
+
16
+ // Open the DevTools.
17
+ // win.webContents.openDevTools();
18
+ };
19
+
20
+ // This method will be called when Electron has finished
21
+ // initialization and is ready to create browser windows.
22
+ // Some APIs can only be used after this event occurs.
23
+ app.whenReady().then(createWindow);
24
+
25
+ // Quit when all windows are closed.
26
+ app.on('window-all-closed', () => {
27
+ // On macOS it is common for applications and their menu bar
28
+ // to stay active until the user quits explicitly with Cmd + Q
29
+ if (process.platform !== 'darwin') {
30
+ app.quit();
31
+ }
32
+ });
33
+
34
+ app.on('activate', () => {
35
+ // On macOS it's common to re-create a window in the app when the
36
+ // dock icon is clicked and there are no other windows open.
37
+ if (BrowserWindow.getAllWindows().length === 0) {
38
+ createWindow();
39
+ }
40
+ });
41
+
42
+ // In this file you can include the rest of your app's specific main process
43
+ // code. You can also put them in separate files and require them here.
@@ -0,0 +1,3 @@
1
+ export default {
2
+ // apiUrl: 'https://prod.com'
3
+ };
@@ -0,0 +1,3 @@
1
+ export default {
2
+ // apiUrl: 'http://test:1234'
3
+ };
@@ -0,0 +1,3 @@
1
+ export default {
2
+ // apiUrl: 'http://localhost:1234'
3
+ };
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="400px" height="247px" viewBox="0 0 400 247" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4
+ <rect id="Rectangle" fill="#FF6347" x="0" y="0" width="400" height="247"></rect>
5
+ <path d="M164.382947,200 L164.382947,182.969188 L94.5504862,182.969188 L94.5504862,48 L77,48 L77,200 L164.382947,200 Z M209.459985,200 L239.942408,72.2689076 L240.311892,72.2689076 L270.424832,200 L288.714286,200 L324,48 L306.449514,48 L279.66193,174.453782 L279.292446,174.453782 L250.103216,48 L231.074794,48 L201.516081,174.453782 L201.146597,174.453782 L175.282723,48 L157.362752,48 L191.170531,200 L209.459985,200 Z" id="LW" fill="#FFFFFF" fill-rule="nonzero"></path>
6
+ </g>
7
+ </svg>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>${project.name}</title>
6
+ <script type="module" src="${project.name}.js"></script>
7
+ <link rel="stylesheet" href="${project.name}.css">
8
+ <link rel="icon" type="image/svg+xml" href="favicon.svg">
9
+ </head>
10
+ <body style="opacity: 0;" onload="document.body.style.opacity=1">
11
+ <${project.name}-root></${project.name}-root>
12
+ </body>
13
+ </html>
@@ -0,0 +1,74 @@
1
+ // Please don't modify this file. Create one outside of the lib directory with
2
+ // your project speicific configurations. Files in the lib directory is subject
3
+ // to overwrite on Leanweb upgrade.
4
+
5
+ class APIClient {
6
+ constructor(baesUrl, sendToken = false, defaultHeaders = {}) {
7
+ this.baesUrl = baesUrl;
8
+ this.sendToken = sendToken;
9
+ this.defaultHeaders = defaultHeaders;
10
+ }
11
+
12
+ async _fetch(method, url = '', data = {}, headers = {}) {
13
+ if (!url.toLowerCase().startsWith('https://') && !url.toLowerCase().startsWith('http://')) {
14
+ url = this.baesUrl + url;
15
+ }
16
+
17
+ if (method === 'GET' && data && typeof data === 'object') {
18
+ // encode data and append to url
19
+ const queryString = paramsToQueryString(data);
20
+ data = null;
21
+ if (url.endsWith('?')) {
22
+ url += queryString;
23
+ } else if (url.indexOf('?') >= 0) {
24
+ url += ('&' + queryString);
25
+ } else {
26
+ url += ('?' + queryString);
27
+ }
28
+ }
29
+
30
+ if (this.sendToken) {
31
+ const token = localStorage.getItem('access_token');
32
+ if (token) {
33
+ headers['access_token'] = token;
34
+ } else {
35
+ return null;
36
+ }
37
+ }
38
+ const response = await fetch(url, {
39
+ method,
40
+ headers: { ...this.defaultHeaders, ...headers },
41
+ body: data ? JSON.stringify(data) : null,
42
+ });
43
+ return response.json();
44
+ }
45
+
46
+ post(url, data, headers) { return this._fetch('POST', url, data, headers); }
47
+ get(url, data, headers) { return this._fetch('GET', url, data, headers); }
48
+ patch(url, data, headers) { return this._fetch('PATCH', url, data, headers); }
49
+ delete(url, data, headers) { return this._fetch('DELETE', url, data, headers); }
50
+ put(url, data, headers) { return this._fetch('PUT', url, data, headers); }
51
+ options(url, data, headers) { return this._fetch('OPTIONS', url, data, headers); }
52
+ }
53
+
54
+ const paramsToQueryString = (params) => {
55
+ return Object.keys(params).map(k => {
56
+ const v = params[k];
57
+ if (Array.isArray(v)) {
58
+ return v.reduce((vacc, vcurr) => {
59
+ return `${vacc}${k}=${encodeURIComponent(vcurr)}&`;
60
+ }, '');
61
+ } else {
62
+ return `${k}=${encodeURIComponent(v)}&`;
63
+ }
64
+ }).reduce((acc, curr) => acc + curr, '').slice(0, -1);
65
+ };
66
+
67
+ // const apiUrl = 'http://localhost:1234';
68
+ // const anotherApiUrl = 'http://127.0.0.1:4321';
69
+
70
+ // export const api = new APIClient(apiUrl, true);
71
+ // export const http = new APIClient(apiUrl);
72
+
73
+ // export const anotherApi = new APIClient(anotherApiUrl, true);
74
+ // export const anotherHttp = new APIClient(anotherApiUrl);