Babel plugin error: Don't use `path.replaceWith()` with a source string, use `path.replaceWithSourceString()` - babeljs

EDIT / UPDATE:
I have taken loganfsmyth's advice and pulled out babel as the first argument to the sveltify function and I can access / console log babel.template.statement.ast however, if I try to call that function my app hangs indefinitely.
Thie details:
I am trying to use this with svelte to replace the import statement and I have a plugin as:
const sveltify = (babel) => ({
visitor: {
ImportDeclaration(path){
// import x from 'svelte/somewhere'
if (path.node.source.value.startsWith("svelte/")) {
const specifiers = path.node.specifiers.map(s => ` ${s.local.name}` );
const importee = path.node.source.value.replace('/', '.');
// this line works without babel.template.statement.ast but gives the error in the question title
// adding babel.template.statement.ast causes the app to hang indefinitely
const importNode = babel.template.statement.ast`const {${specifiers} } = ${importee};`;
path.replaceWith(importNode);
}
}
}
});
and my babel options:
const SVELTE_OPTIONS = {
presets: [
// [
// Babel.availablePresets['env'],
// {
// useBuiltIns: 'usage',
// corejs: 3,
// }
// ],
['es2017', { 'modules': false }],
],
plugins: [
sveltify,
'transform-modules-commonjs',
'transform-destructuring',
'proposal-object-rest-spread',
],
};
And finally I am using that in my code later on a call to transform like this:
// simplified
function transpile(moduleCode) {
const { code } = Babel.transform(moduleCode, SVELTE_OPTIONS);
return code;
}

The other question you linked is pulling babel out of the first param of the plugin, and you should be doing the same, so
const sveltify = () => ({
should be
const sveltify = (babel) => ({
then you can use babel.template from that object.

I think the problem was in the way I was calling ast. The following works:
const sveltify = (babel) => ({
visitor: {
ImportDeclaration(path){
// import x from 'svelte/somewhere'
if (path.node.source.value.startsWith("svelte/")) {
const specifiers = path.node.specifiers.map(s => ` ${s.local.name}` );
const importee = path.node.source.value.replace('/', '.');
const tmpNode = `const {${specifiers} } = ${importee};`;
const importNode = babel.template.statement.ast(tmpNode);
path.replaceWith(importNode);
}
}
}
});

Related

How to insert a draft-js custom component/block

I'm trying to insert my custom block to the editorState of draft-js's editor. I can't seem to find any detailed information on how to accomplish this.
Block Renderer:
const blockRendererFn = (contentBlock) => {
const type = contentBlock.getType();
if (type === 'CustomTestChipBlock') {
return {
component: CustomTestChipBlock,
editable: false,
props: {
foo: 'bar',
},
};
}
}
Block Render Map:
import { DefaultDraftBlockRenderMap } from "draft-js";
import { Map } from 'immutable';
const blockRenderMap = Map({
CustomTestChipBlock: {
element: 'div',
}
}).merge(DefaultDraftBlockRenderMap);
My custom block (material ui chip):
import { Chip } from "#mui/material";
const CustomTestChipBlock = (props) => {
const { block, contentState } = props;
const { foo } = props.blockProps;
const data = contentState.getEntity(block.getEntityAt(0)).getData();
console.log("foo: "+foo)
console.log("data: "+data)
return (
<Chip label="test" size="small"/>
)
}
Now my problem is when I try to insert my custom block. I assume my method of insertion must be wrong. I tried multiple insertion methods but due to lack of any detailed information on the subject, all of them ended up not even running the console.log inside my custom component.
Insertion:
const addChip = () => {
setEditorState(insertBlock("CustomTestChipBlock"));
}
const insertBlock = (type) => {
// This is where I can't find any detailed info at all
const newBlock = new ContentBlock({
key: genKey(),
type: type,
text: "",
characterList: List(),
});
const contentState = editorState.getCurrentContent();
const newBlockMap = contentState.getBlockMap().set(newBlock.key, newBlock);
const newEditorState = ContentState.createFromBlockArray(
newBlockMap.toArray()
)
.set("selectionBefore", contentState.getSelectionBefore())
.set("selectionAfter", contentState.getSelectionAfter());
return EditorState.push(editorState, newEditorState, "add-chip");
};

vscode extension is it possible to pass args into a command markdown link?

I'm working on a vscode extension using the HoverProvider to supply some HTML links for the MarkdownString, the links themselves are my own commands that work fine (they are being registered and their function hits). Unfortunately I'm unable to pass any querystring values/arguments into the command function.
Is it possible to pass args via the MarkdownString so that the command function receives them?
package.json
{
"name": "hover-command",
.. snip snip ...
"contributes": {
"commands": [
{
"command": "hover-command.say_hello",
"title": "greetz"
}
]
},
In the extension.ts file
context.subscriptions.push(
vscode.commands.registerCommand("say_hello", async (hi: string) => {
vscode.window.showInformationMessage(hi + ' greetz at ' + new Date());
})
);
and
const selector: vscode.DocumentSelector = {
scheme: "file",
language: "*",
};
vscode.languages.registerHoverProvider(selector, {
provideHover(
doc: vscode.TextDocument,
pos: vscode.Position,
token: vscode.CancellationToken
): vscode.ProviderResult<vscode.Hover> {
return new Promise<vscode.Hover>((resolve, reject) => {
const hoverMarkup = "[Greetings...](command:say_hello?hi=world)";
if (hoverMarkup) {
const mdstring = new vscode.MarkdownString(hoverMarkup);
mdstring.isTrusted = true; // NOTE: this is needed to execute commands!!
resolve(new vscode.Hover(mdstring));
} else {
reject();
}
}
);
},
});
but the registered command vscode.window.showInformationMessage is not getting any arguments/query string values. I have tried looking at arguments but still at a loss.
A few examples from the VSC source code
[search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22)
[Initialize Repository](command:git.init?%5Btrue%5D)
[configure](command:workbench.action.openSettings?%5B%22editor.formatOnSave%22%5D)
Thanks again #rioV8, after a few failed attempts there are a few steps to get command hover markdown links to work with arguments.
I'm using TypeScript, so I'll add an interface to define the shape of the args
interface ISayHelloArgs {
msg: string;
}
The registered command then uses this interface (you get a single object 'args')
context.subscriptions.push(
vscode.commands.registerCommand("say_hello", async (args: ISayHelloArgs) => {
vscode.window.showInformationMessage(args.msg + ' greetz at ' + new Date());
})
);
The registered HoverProvider then build the args using encodeURI version of a JSON string.
vscode.languages.registerHoverProvider(selector, {
provideHover(
doc: vscode.TextDocument,
pos: vscode.Position,
token: vscode.CancellationToken
): vscode.ProviderResult<vscode.Hover> {
return new Promise<vscode.Hover>((resolve, reject) => {
const args: ISayHelloArgs = { msg: 'hello' };
const jsonArgs = JSON.stringify(args);
const hoverMarkup = `[Greetings...](command:say_hello?${encodeURI(jsonArgs)})`;
if (hoverMarkup) {
const mdstring = new vscode.MarkdownString(hoverMarkup);
mdstring.isTrusted = true; // NOTE: this is needed to execute commands!!
resolve(new vscode.Hover(mdstring));
} else {
reject();
}
}
);
},
});
This worked for me, hope it helps others.

Error: Cannot find module 'react-bootstrap/lib/Breadcrumb'

I am following this tutorial: https://youtu.be/YPbgjPPC1d0 at 34:55 I am trying to use "truffle test", but keep getting Error: Cannot find module 'react-bootstrap/lib/Breadcrumb'
My code:
const { should, assert } = require('chai')
const { useReducer } = require('react')
const { Item } = require('react-bootstrap/lib/Breadcrumb')
const color = artifacts.require('./color.sol')
require('Chair')
useReducer(require('chai-as-promised'))
should()
contract ('color', (accounts) => {
describe('deployment', async () => {
It('deploys successfully', async () => {
contract = await color.deployed()
const address = contract.address
console.log(address)
assert.notEqual(address, '')
})
})
})
I am using Virtual Studio Code
Has anyone had similar problem or know how to fix it? Thanks

How to properly use jasmine-marbles to test multiple actions in ofType

I have an Effect that is called each time it recives an action of more than one "kind"
myEffect.effect.ts
someEffect$ = createEffect(() =>
this.actions$.pipe(
ofType(fromActions.actionOne, fromActions.actionTwo),
exhaustMap(() => {
return this.myService.getSomeDataViaHTTP().pipe(
map((data) =>
fromActions.successAction({ payload: data})
),
catchError((err) =>
ObservableOf(fromActions.failAction({ payload: err }))
)
);
})
)
);
in my test I tried to "simulate the two different actions but I always end up with an error, while if I try with one single action it works perfectly
The Before Each part
describe('MyEffect', () => {
let actions$: Observable<Action>;
let effects: MyEffect;
let userServiceSpy: jasmine.SpyObj<MyService>;
const data = {
// Some data structure
};
beforeEach(() => {
const spy = jasmine.createSpyObj('MyService', [
'getSomeDataViaHTTP',
]);
TestBed.configureTestingModule({
providers: [
MyEffect,
provideMockActions(() => actions$),
{
provide: MyService,
useValue: spy,
},
],
});
effects = TestBed.get(MyEffect);
userServiceSpy = TestBed.get(MyService);
});
This works perfectly
it('should return successActionsuccessAction', () => {
const action = actionOne();
const outcome = successAction({ payload: data });
actions$ = hot('-a', { a: action });
const response = cold('-a|', { a: data });
userServiceSpy.getSomeDataViaHTTP.and.returnValue(response);
const expected = cold('--b', { b: outcome });
expect(effects.someEffect$).toBeObservable(expected);
});
This doesn't work
it('should return successAction', () => {
const actions = [actionOne(), actionTwo()];
const outcome = successAction({ payload: data });
actions$ = hot('-a-b', { a: actions[0], b: actions[1] });
const response = cold('-a-a', { a: data });
userServiceSpy.getSomeDataViaHTTP.and.returnValue(response);
const expected = cold('--b--b', { b: outcome });
expect(effects.someEffect$).toBeObservable(expected);
});
There are two problems in this code.
It suggests that getSomeDataViaHTTP returns two values. This is wrong, the response is no different from your first example: '-a|'
It expects the second successAction to appear after 40 ms (--b--b, count the number of dashes). This is not correct, because actionTwo happens after 20 ms (-a-a) and response takes another 10 ms (-a). So the first successAction is after 20ms (10+10), the second is after 30ms (20+10). The marble is: '--b-b'.
Input actions : -a -a
1st http response : -a
2nd http response : -a
Output actions : --b -b
The working code:
it('should return successAction', () => {
const actions = [actionOne(), actionTwo()];
actions$ = hot('-a-b', { a: actions[0], b: actions[1] });
const response = cold('-a|', { a: data });
userServiceSpy.getSomeDataViaHTTP.and.returnValue(response);
const outcome = successAction({ payload: data });
const expected = cold('--b-b', { b: outcome });
expect(effects.someEffect$).toBeObservable(expected);
});
Marble testing is cool but it involves some black magic you should prepare for. I'd very much recommend you to carefully read this excellent article to have a deeper understanding of the subject.

redux observable with axios onProgress

i am creating an upload function that will show a progress bar to the client inside a React Redux and Redux-observable, and i use axios to do a put request to AWS S3.
My epics is as follow
...
function uploadFile(mimetype, url, file) {
const config = {
headers: {
'Content-Type': mimetype,
},
onUploadProgress(progress) {
const percentCompleted = Math.round((progress.loaded * 100) / progress.total)
uploadProgress(percentCompleted)
},
}
axiosRetry(axios, { retries: 3 })
return axios.put(url, file[0], config)
}
export const uploadEpic = (action$, store) => action$
.ofType(SIGNED_URL_SUCCESS)
.mergeMap(() => {
const file = store.getState().File.droppedFile
const mimetype = file[0].type
const { url } = store.getState().SignedUrl
const { fileData } = store.getState().Upload
return of(uploadFile(mimetype, url.data, file))
.concatMap(() => {
const uploadedData = {
url: fileData.url,
thumbUrl: `${fileData.folder}/${fileData.filename}-00001.png`,
}
return [
upload(uploadedData),
uploadSuccess(),
]
})
.catch(error => of(uploadFailure(error)))
})
export default uploadEpic
The upload seems to work, as i received an AWS SNS email telling that its done, but i can't seem to see that it is updating the Upload.progress state inside my Upload reducer.
The reason i am using axios is particulary because its axios-retry and its onUploadProgress, since i can't seem to find an example doing an onProgress using universal-rx-request
so two questions probably
How can i achieve this using axios
How can i achieve this using universal-rx-request
Thanks to this SO answer
I ended up not using axios at all
I got it working with this
import { of } from 'rxjs/observable/of'
import { Subject } from 'rxjs/Subject'
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/dom/ajax'
import { SIGNED_URL_SUCCESS } from 'ducks/SignedUrl'
import {
upload,
uploadIsLoading,
uploadSuccess,
uploadFailure,
uploadProgress,
} from 'ducks/Upload'
export const uploadEpic = (action$, store) => action$
.ofType(SIGNED_URL_SUCCESS)
.mergeMap(() => {
const file = store.getState().File.droppedFile
const mimetype = file[0].type
const { url } = store.getState().SignedUrl
const { fileData } = store.getState().Upload
const progressSubscriber = new Subject()
const request = Observable.ajax({
method: 'PUT',
url: url.data,
body: file[0],
headers: {
'Content-Type': mimetype,
},
progressSubscriber,
})
const requestObservable = request
.concatMap(() => {
const uploadedData = {
...
}
return [
upload(uploadedData),
uploadIsLoading(false),
uploadSuccess(),
]
})
.catch(error => of(uploadFailure(error)))
return progressSubscriber
.map(e => ({ percentage: (e.loaded / e.total) * 100 }))
.map(data => uploadProgress(data.percentage))
.merge(requestObservable)
})
UPDATE: on rxjs 6 the merge operators is deprecated, so if you're using rxjs 6, change the code above to
// some/lib/folder/uploader.js
import { of, merge } from 'rxjs' // import merge here
import { ajax } from 'rxjs/ajax'
import { map, catchError } from 'rxjs/operators' // instead of here
import { Subject } from 'rxjs/Subject'
export function storageUploader(...args) {
const progressSubscriber = new Subject()
const request = ajax({...someRequestOptions})
.pipe(
map(() => success()),
catchError((error) => of(failure(error))),
)
const subscriber = progressSubscriber
.pipe(
map((e) => ({ percentage: (e.loaded / e.total) * 100 })),
map((upload) => progress(upload.percentage)),
catchError((error) => of(failure(error))),
)
return merge(subscriber, request) // merge both like this, instead of chaining the request on progressSubscriber
}
//the_epic.js
export function uploadEpic(action$, state$) {
return action$
.pipe(
ofType(UPLOAD),
mergeMap((someUploadOptions) => uploaderLib(
{ ...someUploadOptions },
actionSuccess,
actionFailure,
actionProgress,
)),
catchError((error) => of(actionFailure(error))),
)
}