Sapper. How to globally use third-party libraries - javascript

I want to access a variable gun in server and client side.
This is my module:
import Gun from 'gun/gun'
import Sea from 'gun/sea' // eslint-disable-line no-unused-vars
export const gun = Gun({
localStorage: true,
radisk: true,
peers: ['http://localhost:8765/gun']
})
If it was a Nuxt, which I want to abandon in favor of a Sapper, I would have implemented it like this:
import Gun from 'gun/gun'
import 'gun/sea'
import 'gun/lib/open'
const gun = Gun({
localStorage: true,
radisk: true,
peers: ['http://localhost:8765/gun']
})
export default ({ app }, inject) => {
inject('gun', () => gun)
}
// nuxt.config.js
...
plugins: [{ src: '#/plugins/gun.js' }]
...
Thus, I would get access to the $gun everywhere:
On the server side:
export default {
asyncData(context){
context.app.$gun()
}
}
And on the client side:
methods: {
submit() {
const gun = this.$gun()
const user = this.$gun().user()
...
}
}
And also in the template:
<template>
<div>{{ $gun }}</div>
</tempalte>
This question does not concern the use of the specific library that is being discussed in the question (gun). It can be a Websocet connection (then we would pass the ws variable sun in the same way.), or an rpc (to connect with Bitcoin) - I can give many examples where this can be important.
Somewhere I read that you need to implement this through the rollbar, somewhere I read about a regular module (es6 or .svelte) - but then I encounter a number of other problems ...

I don't really see the direct question, but I'll just guess... you're trying to use a global variable in svelte (moving from nuxt)?
Svelte uses rollup, and you should have a .rollup.config.js file in your root.
export default {
...
plugins: [
svelte({
// magic happens here
})
]
}
More documentation on (what I think your issue is) including globals.
https://svelte.dev/docs#Compile_time
&
https://github.com/rollup/rollup-plugin-svelte

Related

How do I access environment variables in Strapi v4?

Strapi Version: 4.3.0
Operating System: Ubuntu 20.04
Database: SQLite
Node Version: 16.16
NPM Version: 8.11.0
Yarn Version: 1.22.19
I have created Preview button for an article collection type. I'm using the Strapi blog template. I managed to make the Preview button appear in the Content Manager. I hard coded the link to be opened when you click the Preview button and it works. Now, I want the plugin to use a link with environment variables instead of a hard coded link. I don't know how I can access the environment variables in the source code for the plugin.
My objective:
I want to replace
href={`http://localhost:3000?secret=abc&slug=${initialData.slug}`}
with
href={${CLIENT_FRONTEND_URL}?secret=${CLIENT_SECRET}&slug=${initialData.slug}`}
in ./src/plugins/previewbtn/admin/src/components/PreviewLink/index.js
where CLIENT_FRONTEND_URL and CLIENT_SECRET are environment variables declared like so in .env:
CLIENT_FRONTEND_URL=http://localhost:3000
CLIENT_PREVIEW_SECRET=abc
Here's a rundown of the code I used:
First, I created a strapi app using the blog template, then created a plugin.
// Create strapi app named backend with a blog template
$ yarn create strapi-app backend --quickstart --template #strapi/template-blog#1.0.0 blog && cd backend
// Create plugin
$ yarn strapi generate
Next, I created a PreviewLink file to provide a link for the Preview button
// ./src/plugins/previewbtn/admin/src/components/PreviewLink/index.js
import React from 'react';
import { useCMEditViewDataManager } from '#strapi/helper-plugin';
import Eye from '#strapi/icons/Eye';
import { LinkButton } from '#strapi/design-system/LinkButton';
const PreviewLink = () => {
const {initialData} = useCMEditViewDataManager();
if (!initialData.slug) {
return null;
}
return (
<LinkButton
size="S"
startIcon={<Eye/>}
style={{width: '100%'}}
href={`http://localhost:3000?secret=abc&slug=${initialData.slug}`}
variant="secondary"
target="_blank"
rel="noopener noreferrer"
title="page preview"
>Preview
</LinkButton>
);
};
export default PreviewLink;
Then I edited this pregenerated file in the bootstrap(app) { ... } section only
// ./src/plugins/previewbtn/admin/src/index.js
import { prefixPluginTranslations } from '#strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import Initializer from './components/Initializer';
import PreviewLink from './components/PreviewLink';
import PluginIcon from './components/PluginIcon';
const name = pluginPkg.strapi.name;
export default {
register(app) {
app.addMenuLink({
to: `/plugins/${pluginId}`,
icon: PluginIcon,
intlLabel: {
id: `${pluginId}.plugin.name`,
defaultMessage: name,
},
Component: async () => {
const component = await import(/* webpackChunkName: "[request]" */ './pages/App');
return component;
},
permissions: [
// Uncomment to set the permissions of the plugin here
// {
// action: '', // the action name should be plugin::plugin-name.actionType
// subject: null,
// },
],
});
app.registerPlugin({
id: pluginId,
initializer: Initializer,
isReady: false,
name,
});
},
bootstrap(app) {
app.injectContentManagerComponent('editView', 'right-links', {
name: 'preview-link',
Component: PreviewLink
});
},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};
And lastly this created this file to enable the plugin Reference
// ./config/plugins.js
module.exports = {
// ...
'preview-btn': {
enabled: true,
resolve: './src/plugins/previewbtn' // path to plugin folder
},
// ...
}
I solved this by adding a custom webpack configuration to enable Strapi's admin frontend to access the environment variables as global variables.
I renamed ./src/admin/webpack.example.config.js to ./src/admin/webpack.config.js. Refer to the v4 code migration: Updating the webpack configuration from the Official Strapi v4 Documentation.
I then inserted the following code, with help from Official webpack docs: DefinePlugin | webpack :
// ./src/admin/webpack.config.js
'use strict';
/* eslint-disable no-unused-vars */
module.exports = (config, webpack) => {
// Note: we provide webpack above so you should not `require` it
// Perform customizations to webpack config
// Important: return the modified config
config.plugins.push(
new webpack.DefinePlugin({
CLIENT_FRONTEND_URL: JSON.stringify(process.env.CLIENT_FRONTEND_URL),
CLIENT_PREVIEW_SECRET: JSON.stringify(process.env.CLIENT_PREVIEW_SECRET),
})
)
return config;
};
I rebuilt my app afterwards and it worked.
You shouldn't have to change the webpack config just find .env file in the root directory
add
AWS_ACCESS_KEY_ID = your key here
then just import by
accessKeyId: env('AWS_ACCESS_KEY_ID')

How to extend core modules of Vue Storefront

I want to override an action from cart module store. I am trying to extend this CartModule by following this link
Extending and Overriding Modules Doc
I have created a file /src/modules/cart/index.ts with following code
import { VueStorefrontModuleConfig, extendModule, VueStorefrontModule } from '#vue-storefront/core/lib/module'
import { CartModule } from '#vue-storefront/core/modules/cart'
import { cartModule } from './store'
const cartExtend: VueStorefrontModuleConfig = {
key: 'cart',
store: {modules: [{key: 'cart', module: cartModule}]},
afterRegistration: function () {
console.log('Cart module extended')
}
}
extendModule(cartExtend)
export const registerModules: VueStorefrontModule[] = [CartModule]
I am getting error that CarModule type does not match with VueStorefrontModule
Also I don't know what to do next in order to make it effective. Docs are not clear about it. Please help. Thanks
If you want to overwrite action of module you don't want to extend module but store.
Here is example:
Vuestorefront has CartModule (in core) and you need to change code of action refreshTotals.
Code your in file /src/modules/cart/index.ts:
import {StorefrontModule} from '#vue-storefront/core/lib/modules';
import {extendStore} from '#vue-storefront/core/helpers';
const cartModule = {
action: {
async refreshTotals({dispatch}, payload) {
//
// your new (and better!) code ;-)
//
}
},
}
export const MyAwesomeCart: StorefrontModule = function () {
extendStore('cart', cartModule);
}
In last step register this your new module under /src/modules/client.ts:
..
...
import {CartModule} from '#vue-storefront/core/modules/cart';
import {MyAwesomeCart} from "modules/cart/index";
export function registerClientModules() {
registerModule(CartModule); // original module
registerModule(MyAwesomeCart); // your new overwiritng module
...
..

Unable to use API key as env variable with Butter CMS library inside asyncData()

I recently converted my site from Drupal to Vue, and it's currently running as a regular Vue app. For SEO (and other reasons), I'm working on converting it to use Nuxt, and I'm having trouble figuring out how to set the private API key as an environment variable and use it in a component with the Butter library and asyncData(). Using the Butter docs for Vue, I have it working fine in a SPA, but I can't get the same thing to work in Nuxt.
In my SPA, I just added API_KEY to dev.env.js, and then I have these two lines in buttercms.js:
import Butter from 'buttercms'
export const butter = Butter(process.env.API_KEY)
and then in my component:
<script>
import { butter } from "#/buttercms";
...
methods: {
getPost() {
butter.post.retrieve(this.$route.params.slug)
.then(res => {
this.post = res.data;
})
.catch(res => {
console.log(res);
});
}
},
which works fine. In Nuxt, I set my API key in nuxt.config.js like so:
env: {
API_KEY: process.env.API_KEY || '1234567890'
},
the same contents for buttercms.js as listed above, and then in my component:
<script>
import { butter } from "buttercms";
export default {
asyncData(context) {
return butter.page
.retrieve("static_page", "about-smga")
.then(res => {
console.log(res.data.data);
return {
page: res.data.data
};
})
.catch(res => {
console.log(res);
});
},
...
</script>
However, when I run it, I get an error that says Cannot read property 'page' of undefined, which tells me that the Butter library isn't being used. Obviously I'm doing something wrong, but I'm not sure what. What do I need to change to be able to use butter in my asyncData() call?
You are importing butter class from buttercms module in your component. But its not initialized. You can put your initialization into plugins/buttercms.js and add it into nuxt.config into plugin section.
import Butter from 'buttercms'
export default (ctx, inject) => {
inject('butter', Butter(process.env.API_KEY))
}
And then you can reference initialzied instance via this.$butter in your components

Angular 4 - How to Simulate Mock Data for Prototyping and Development

I'm in the process of upgrading an AngularJS v1.5 project to Angular 4.x. During development of the original AngularJS application, we would use the ngMocks package to simulate actual web service API responses, and display the data accordingly on the page. This was incredibly helpful during development as I didn't have to hard-code values for removal later on. Best of all, we configured Webpack to only include the mock data during development, and ignore those mock data files when building our application for production use. The mock data was configured like this:
/* app-login.mock.js */
import angular from 'angular';
import 'angular-mocks';
angular.module('app').run(function ($httpBackend) {
$httpBackend
.whenPOST('./api/auth')
.respond(function(method, url, data) {
var credentials = angular.fromJson(data);
if (credentials.username == 'gooduser') {
return [200, {'token': createToken(credentials.username)}];
} else {
return [401, {'errorMsg': 'Mock login only allows username "gooduser"'}];
}
});
});
function createToken(username) {
// Create a token, which is valid enough for testing purposes.
// This token is based on the actual token generated by the web service.
let currentTime = new Date();
let futureTime = new Date(currentTime.getTime() + ((currentTime.getHours() + 8) * 60 * 60 * 1000));
let header = {
alg: 'HS512'
};
let payload = {
exp: futureTime.getTime() / 1000,
sub: username,
roles: 'SOME_APPLICATION_ROLES',
iat: currentTime.getTime() / 1000
};
return `${btoa(angular.toJson(header))}.${btoa(angular.toJson(payload))}`;
}
Webpack was then configured to include all "mock" files into the built bundle, which could then be displayed as if it were a real HTTP response.
/* webpack.config.js */
const isProd = process.env.NODE_ENV === 'production';
const entry = {
app: (() => {
let app = [
'babel-polyfill',
path.join(PATHS.app, 'pollyfills.ts'),
path.join(PATHS.app, 'main.ts')
];
if (isProd) {
app.push(path.join(PATHS.app, 'app.prod.js'));
} else {
app.push(path.join(PATHS.app, 'app.mock.js'));
}
return app;
})()
};
module.exports = {
entry,
// ...other exports
};
And then the app.mock.js file:
/* app.mock.js */
var mockContext = require.context(".", true, /\.mock$/);
mockContext.keys().forEach(mockContext);
I've scoured the internet looking for a solution that works just as well as our old one, though I haven't come up with any good answers. Best I've found are tutorials on how to set up Unit Tests that return mock data, and while that's useful for testing functionality it doesn't help me test the application during the development process.
I also have seen some documentation on setting up Interceptors using the new HttpClient class found within Angular 4, but I'm not sure how to add it to our Webpack configuration under the condition of only being allowed during development. Does anyone have any advice on what to do?
I use the angular-in-memory-web-api. You can find it here: https://github.com/angular/in-memory-web-api
UPDATE: The repo was moved here, within the angular/angular repo: https://github.com/angular/angular/tree/e0dfa42d6e656124f3c3d78e178b1bf091b38e79/packages/misc/angular-in-memory-web-api
It intercepts all of your http calls and works with sample data you provide.
To change from dev to production, you need to remove the imports. Or you could possibly write two different modules, one with the dev imports and one with the production imports and include one or the other with webpack similar to what you do now. (But I have not tried this.)
You set up your data like this:
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { IProduct } from './product';
export class ProductData implements InMemoryDbService {
createDb() {
let products: IProduct[] = [
{
'id': 1,
'productName': 'Leaf Rake',
'productCode': 'GDN-0011',
'releaseDate': 'March 19, 2016',
'description': 'Leaf rake with 48-inch wooden handle.',
'price': 19.95,
'starRating': 3.2,
'imageUrl': 'http://openclipart.org/image/300px/svg_to_png/26215/Anonymous_Leaf_Rake.png',
'tags': ['rake', 'leaf', 'yard', 'home']
},
// ...
];
return { products };
}
}
And you build your data access service using the normal Http or HttpClient.
I have a full example with all CRUD operations here: https://github.com/DeborahK/Angular2-ReactiveForms

How to bind console.log to "l" in vue.js?

main.js has this code
window.l = function () { }
try {
window.l = console.log.bind(console)
} catch (e) { }
which works in non-Vue apps. However, when calling
l("test")
from a Vue action/method, it complains it isn't defined.
How can that work?
Reasoning: need to output some debugging data, with as less typing as possible.
When you want to add global-level functionalities to Vue, you should generally use mixins or plugins.
For the next examples, I assume you are using vue-cli with the complete webpack template. Moreover, we will use App.vue as a practical reference, but you can apply the same principles to other components...
Mixins
Create a mixin named log.js (in a mixins folder) with the following code:
export default {
methods: {
l (...args) { // rest parameters
console.log(...args) // spread operator
}
}
}
Open App.vue, import your mixin and use it:
<script>
import log from './mixins/log'
export default {
name: 'app',
mixins: [log],
created () {
this.l('Foo', 'Bar') // Foo Bar
}
}
</script>
Plugins
Create a plugin named log.js (in a plugins folder) with the following code:
export default {
install (Vue, options) {
Vue.prototype.$l = console.log.bind(console)
Vue.l = console.log.bind(console)
}
}
Open your main.js and declare your global plugin:
import log from './plugins/log'
Vue.use(log)
Open App.vue, import Vue and use your plugin:
<script>
import Vue from 'vue'
export default {
name: 'app',
created () {
this.$l('Foo') // Foo
Vue.l('Bar') // Bar
}
}
</script>
You might say: "Hey, why should I have to write this or Vue? I just wanna write l, that's all!". Well... This is actually how Vue has been designed. In order to provide global functionalities (shared by all components), you have to add static properties to the Vue object or prototype properties (Vue.prototype) that are accessible through this in Vue instances.
EDIT
I have just thought about an alternative solution...
You can edit your index.html to add this:
<script>
var l = console.log.bind(console)
</script>
Then, to avoid ESLint errors, you should also edit your .eslintrc.js file to reference your new global variable:
globals: {
l: true
}
The file looks like this:
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
globals: {
l: true
},
env: {
browser: true,
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}
Restart your dev server. Now you should be able to use l in your code:
<script>
export default {
name: 'app',
created () {
l('It works!')
}
}
</script>
Assign console.log like this.
window.l=console.log;

Categories

Resources